如何在 Perl 中限制 CGI 文件上传的文件类型?

发布于 2024-07-15 19:46:04 字数 315 浏览 7 评论 0原文

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

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

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

I am using CGI to allow the user to upload some files. I just want the just to be able to upload .txt or .csv files. If the user uploads file with any other format then I want to be able to put out an error message.

I saw that this can be done by javascript: http://www.codestore.net/store.nsf/unid/DOMM-4Q8H9E

But is there a better way to achieve this? Is there is some functionality in Perl that allows this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一个人的旅程 2024-07-22 19:46:04

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

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

如果您确实想正确执行此操作,请让用户上传文件,然后
然后使用类似 File::MimeInfo::Magic (或 <代码>文件(1),
UNIX 实用程序)来猜测实际的文件类型。 如果你不喜欢
文件类型,删除该文件并向用户提供错误消息。

The disclaimer on the site to you link to is important:

Note: This is not entirely foolproof as people can easily change the extension of a file before uploading it, or do some other trickery, as in the case of the "LoveBug" virus.

If you really want to do this right, let the user upload the file, and
then use something like File::MimeInfo::Magic (or file(1), the
UNIX utility) to guess the actual file type. If you don't like the
file type, delete the file and give the user an error message.

酸甜透明夹心 2024-07-22 19:46:04

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

听起来很容易,不是吗? 它不是。 然后还有一些。

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

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

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

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

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

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

现在事情变得非常糟糕了。 假设您已接受上传并将其存储为 /data/mygoodfilename.txt,并且 Web 服务器正确地将其作为内容类型“text/plain”提供服务。 您认为浏览器会将其解释为什么? 纯文本? 你应该很幸运。

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

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

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

此时,您最后绝望的可能性是这样的:

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

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

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

I just want the just to be able to upload .txt or .csv files.

Sounds easy, doesn't it? It's not. And then some.

The simple approach is just to test that the file ends in ‘.txt’ or ‘.csv’ before storing it on the filesystem. This should be part of a much more in-depth validation of what the filename is allowed to contain before you let a user-submitted filename anywhere near the filesystem.

Because the rules about what can go in a filename are complex on some platforms (especially Windows) it's usually best to create your own filename independently with a known-good name and extension.

In any case there is no guarantee that the browser will send you a file with a usable name at all, and even if it does there is no guarantee that name will have ‘.txt’ or ‘.csv’ at the end, even if it is a text or CSV file. (Some platforms simply do not use extensions for file typing.)

Whilst you can try to sniff the contents of the file to see what type it might be, this is highly unreliable. For example:

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

could be plain text, CSV, HTML, XML, or a variety of other formats. Better to give the user an explicit control to say what file type they're uploading (or use one file upload field per type).

Now here's where it gets really nasty. Say you've accepted the upload and stored it as /data/mygoodfilename.txt, and the web server is correctly serving it as the Content-Type ‘text/plain’. What do you think the browser interprets it as? Plain text? You should be so lucky.

The problem is that browsers (primarily IE) don't trust your Content-Type header, and instead sniff the contents of the file to see if it looks like something else. Serve the above snippet as plain text, and IE will happily treat it as HTML. This can be a huge problem, because HTML can include client-side scripts that will take over the user's access to the site (a cross-site-scripting attack).

At this point you might be tempted to sniff the file on the server-side, for example using the ‘file’ command, to check it doesn't contain ‘<html>’. But this is doomed to failure. The ‘file’ command does not sniff for all the same HTML tags as IE does, and other browsers sniff differently anyway. It's quite easy to prepare a file that ‘file’ will claim is not HTML, but that IE will nevertheless treat as if it is (with security-disaster implications).

Content-sniffing approaches such as ‘file’ will give you only a false sense of security. This is a convenience tool for loose guessing of filetypes and not an effective security measure.

At this point your last desperate possibilities are things like:

  • serving all user-uploaded files from a separate hostname, so that a script injection attack can't purloin the credentials of your main site;

  • serving all user-uploaded files through a CGI wrapper, adding the header ‘Content-Disposition: attachment’ so that browsers won't attempt to display them directly;

  • only accepting uploads from trusted users.

紅太極 2024-07-22 19:46:04

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

此操作的安全性完全是另一回事。

On unix the easiest way is to do an JRockway suggested. If not on unix then your options are limited. You can examine the file extension and you can examine the contents to verify. I'm assuming for you specific case that you only want "* seperated value" text files. So one of the Text::CSV::* modules may be useful in verifying the file is the type you asked for.

Security for this operation is a whole other ball of wax.

庆幸我还是我 2024-07-22 19:46:04

试试这个:

$file_name = "file.txt";

$file_cmd  = "file \"$file_name"\";

$file_type = `$file_cmd`;

return 0 unless($file_type =~ /(ASCII|text)/i)

try this:

$file_name = "file.txt";

$file_cmd  = "file \"$file_name"\";

$file_type = `$file_cmd`;

return 0 unless($file_type =~ /(ASCII|text)/i)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文