【问题标题】:How can I limit file types in CGI file uploads in Perl?如何在 Perl 中限制 CGI 文件上传中的文件类型?
【发布时间】:2012-04-28 05:10:02
【问题描述】:

我正在使用 CGI 来允许用户上传一些文件。我只是希望能够上传 .txt 或 .csv 文件。如果用户上传任何其他格式的文件,那么我希望能够发出错误消息。

我看到这可以通过javascript来完成:http://www.codestore.net/store.nsf/unid/DOMM-4Q8H9E

但是有没有更好的方法来实现这一点? Perl 中是否有一些功能允许这样做?

【问题讨论】:

    标签: perl cgi file-upload


    【解决方案1】:

    您链接到的网站上的免责声明很重要:

    注意:这并不完全是万无一失的,因为人们可以在上传文件之前轻松更改文件的扩展名,或者做一些其他的诡计,例如“LoveBug”病毒。

    如果你真的想这样做,让用户上传文件,然后 然后使用类似File::MimeInfo::Magic(或file(1), UNIX 实用程序)来猜测实际的文件类型。如果你不喜欢 文件类型,删除文件并给用户一个错误信息。

    【讨论】:

    • 关于我给定的环境,他们没有魔法。在 perl 中如何使用 file(1) 实用程序?
    【解决方案2】:

    我只是希望能够上传 .txt 或 .csv 文件。

    听起来很简单,不是吗?它不是。然后是一些。

    简单的方法是在将文件存储到文件系统之前测试文件是否以“.txt”或“.csv”结尾。在您让用户提交的文件名靠近文件系统的任何位置之前,这应该是对文件名允许包含的内容进行更深入验证的一部分。

    由于在某些平台(尤其是 Windows)上,文件名中可以​​包含哪些内容的规则很复杂,因此通常最好使用已知良好的名称和扩展名独立创建自己的文件名。

    在任何情况下,都不能保证浏览器会向您发送一个具有可用名称的文件,即使确实如此,也不能保证该名称的末尾会包含“.txt”或“.csv” ,即使它是文本或 CSV 文件。 (有些平台根本不使用扩展名来输入文件。)

    虽然您可以尝试嗅探文件的内容以查看它可能是什么类型,但这非常不可靠。例如:

    <html>,<body>,</body>,</html>
    

    可以是纯文本、CSV、HTML、XML 或各种其他格式。最好让用户明确控制他们正在上传的文件类型(或为每种类型使用一个文件上传字段)。

    现在这才是真正令人讨厌的地方。假设您已接受上传并将其存储为 /data/mygoodfilename.txt,并且 Web 服务器正确地将其作为 Content-Type 'text/plain' 提供。您认为浏览器将其解释为什么?纯文本?你应该很幸运。

    问题在于浏览器(主要是 IE)不信任您的 Content-Type 标头,而是嗅探文件的内容以查看它是否看起来像其他东西。将上面的 sn-p 作为纯文本提供,IE 会很乐意将其视为 HTML。这可能是一个大问题,因为 HTML 可以包含客户端脚本,这些脚本将接管用户对站点的访问(跨站点脚本攻击)。

    此时,您可能会想在服务器端嗅探文件,例如使用“文件”命令来检查它不包含“”。但这注定要失败。 “file”命令不会像 IE 那样嗅探所有相同的 HTML 标签,并且其他浏览器无论如何都会以不同的方式嗅探。准备一个“文件”声称不是 HTML 的文件很容易,但 IE 仍会将其视为 HTML 文件(具有安全灾难隐患)。

    诸如“文件”之类的内容嗅探方法只会给您一种虚假的安全感。这是一种用于松散猜测文件类型的便捷工具,不是一种有效的工具安全措施。

    此时你最后绝望的可能性是:

    • 从一个单独的主机名提供所有用户上传的文件,以便脚本注入攻击无法盗取您的主站点的凭据;

    • 通过 CGI 包装器提供所有用户上传的文件,添加标题“Content-Disposition: attachment”,以便浏览器不会尝试直接显示它们;

    • 只接受来自受信任用户的上传。

    【讨论】:

    • 哪些浏览器(除了 IE)不尊重 Content-Type: text/plain header?
    • 至少 Firefox 和 Opera(尽管它们的嗅探范围不那么广泛)。 WHATWG 甚至试图对其进行标准化:w3.org/TR/html5/infrastructure.html#content-type-sniffing-0。这一切是多么丑陋和令人沮丧。
    【解决方案3】:

    在 unix 上,最简单的方法是建议 JRockway。如果不在 unix 上,那么您的选择是有限的。您可以检查文件扩展名并检查内容以进行验证。我假设您只需要“* 分隔值”文本文件的特定情况。因此,其中一个 Text::CSV::* 模块可能有助于验证文件是您要求的类型。

    此操作的安全性完全是另一个蜡球。

    【讨论】:

      【解决方案4】:

      试试这个:

      $file_name = "file.txt";
      
      $file_cmd  = "file \"$file_name"\";
      
      $file_type = `$file_cmd`;
      
      return 0 unless($file_type =~ /(ASCII|text)/i)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-13
        • 2011-11-11
        • 1970-01-01
        • 2012-02-27
        • 1970-01-01
        • 2016-09-25
        • 2012-12-06
        • 2010-12-16
        相关资源
        最近更新 更多