TL;DR
Stop.搁置你的脚本现在。这是一个巨大的安全漏洞waiting被剥削。阅读以下资源:
- perlsec http://perldoc.perl.org/perlsec.html
- the CERT Perl 安全编码标准 https://www.securecoding.cert.org/confluence/displa/perl/CERT+Perl+Secure+Coding+Standard(特别是关于输入验证和数据清理 https://www.securecoding.cert.org/confluence/display/perl/01.+Input+Validation+and+Data+Sanitization)
- OWASP 页面无限制文件上传 https://www.owasp.org/index.php/Unrestricted_File_Upload
- 信息安全页面完整的文件上传漏洞 http://resources.infosecinstitute.com/file-upload-vulnerabilities/
- CWE 页面危险类型文件无限制上传 http://cwe.mitre.org/data/definitions/434.html
- SANS 建议实现安全文件上传的 8 条基本规则 http://software-security.sans.org/blog/2009/12/28/8-basic-rules-to-implement-secure-file-uploads.
当你读完并理解了所有这些内容后,停下来想一想,如果你really需要让用户上传文件到你的服务器上。思考long and hard.您真的能解释所有列出的漏洞吗?如果您仍然觉得需要这样做,请考虑寻求安全专家的帮助。遵循上述资源中规定的准则小心并了解设计中的错误可能会损害您的整个网站。
我知道这只是一个测试脚本,而不是一个生产应用程序(至少,我真的希望是这样),但即便如此,你正在做的事情(特别是how你正在做这件事)是一个非常,very 馊主意。这里有一个选择few的
原因,来自 OWASP 页面无限制文件
上传 https://www.owasp.org/index.php/Unrestricted_File_Upload:
- 该网站可能会被篡改。
- Web服务器可以通过上传并执行Web-Shell来受到危害,该Web-Shell可以:运行命令、浏览系统文件、浏览本地资源、攻击其他服务器、利用本地漏洞等。
- 此漏洞可能使网站容易受到 XSS 等其他类型的攻击。
- 可以通过将恶意文件上传到服务器来利用本地文件包含漏洞。
更多来自 OWASP:
上传的文件对应用程序构成重大风险。第一步
很多攻击都是为了获取一些代码到被攻击的系统中。然后攻击
只需要找到一种方法来执行代码即可。使用文件上传有帮助
攻击者完成第一步。
不受限制的文件上传的后果可能会有所不同,包括完全
系统接管、文件系统过载、将攻击转发到后端
系统和简单的破坏。
很可怕的东西,是吧?
存在的问题
你的代码
让我们首先看看您发布的代码存在的一些问题。
没有严格,没有警告
开始推杆use strict; use warnings;
在顶端你的每一个 Perl 脚本
曾经写过。我最近有幸修复了一个 CGI 脚本,其中包含
像这样的片段:
my ($match) = grep { /$usrname/ } @users;
此代码用于检查在 HTML 表单中输入的用户名是否与
有效用户列表。问题一:变量$usrname
曾是
拼写错误(应该是$username
带“e”)。既然严格
检查关闭,Perl 愉快地插入了(未声明的)全局值
多变的$usrname
, or undef
。这把看似无辜的片段变成了这个怪物:
my ($match) = grep { // } @users;
哪个匹配一切在有效用户列表中并返回第一个
匹配。您可以在表单的用户名字段中输入您想要的任何内容
并且脚本会认为您是有效用户。由于警告也已关闭,
在开发过程中从未发现这一点。当您打开警告时,
该脚本仍然会运行并返回一个用户,但你也会得到类似的东西
这:
Name "main::usrname" used only once: possible typo at -e line 1.
Use of uninitialized value $usrname in regexp compilation at -e line 1.
当您还打开严格时,脚本将无法编译,甚至无法运行
全部。此代码片段还存在其他问题(例如,字符串“a”将与用户名“janedoe”匹配),但严格和警告至少提醒我们注意一个主要问题。我怎么强调都不为过:总是,always use strict; use
warnings;
无污染模式
Web 开发的第一条规则是,
“始终清理用户输入。”跟我重复一遍:始终清理用户输入。再一次:始终清理用户输入。换句话说,从来没有
盲目信任用户输入而不首先验证它。用户(即使是那些没有恶意的用户)非常擅长将创意值输入到表单中
可能会破坏您的应用程序(或更糟)的字段。如果你不限制他们的创造力
恶意用户对您的网站造成的损害是没有限制的(请参阅多年生 #1
OWASP Top 10 漏洞,
注射 https://www.owasp.org/index.php/Top_10_2013-A1-Injection).
Perl 的污染模式可以帮助解决这个问题。污点模式强迫你
在某些潜在危险的操作中使用之前检查所有用户输入,例如system()
功能。污点模式就像枪上的安全装置:它可以防止很多痛苦
意外事故(不过如果你真的想搬起石头砸自己的脚,你可以
始终关闭安全性,例如当您在未实际删除危险字符的情况下取消污染变量时)。
打开污点模式您编写的每一个 CGI 脚本。您可以通过传递来启用它-T
标志,像这样:
#!/usr/bin/perl -T
启用污点模式后,如果您尝试执行以下操作,您的脚本将引发致命错误
在危险情况下使用受污染的数据。这是我在互联网上的随机脚本中发现的这种危险情况的示例:
open(LOCAL, ">/home/Desktop/$name") or die $!;
好吧,我撒谎了,该片段不是来自随机脚本,而是来自your代码。孤立地看,这个片段只是在乞求被击中目录遍历攻击 http://en.wikipedia.org/wiki/Directory_traversal_attack,恶意用户在其中输入相对路径以访问他们不应访问的文件。
Fortunately, you've done something right here: you ensured that $name
will contain no directory separators by using a regex*. This is exactly what taint mode would require you to do. The benefit of taint mode is that if you forget to sanitize your input, you will be alerted immediately with an error like this:
Insecure dependency in open while running with -T switch at foo.cgi line 5
与严格一样,污点模式迫使您通过导致程序失败来立即解决代码中的问题,而不是让它静静地跛行。
* 你做了一些thing是的,但你也做了一些things wrong:
- 如果用户只传递一个没有目录分隔符的文件名,你的程序将会死掉,例如
foo
- 您不会删除可由 shell 解释的特殊字符,例如
|
- 你永远不会清理变量
$file
但您稍后尝试在代码中使用它来读取文件
- 您不检查要写入的文件是否已经存在(请参阅下面的“不检查文件是否存在”)
- 您允许用户选择将存储在您的服务器上的文件的名称,这给了他们比您应该熟悉的更多的控制权(请参阅下面的“允许用户设置文件名”)
CGI::鲤鱼 fatalsToBrowser
我会在这个问题上给你一个无罪推论,因为你仍在测试你的脚本,但以防万一你不知道并且因为我已经在谈论 CGI 安全问题,切勿在生产环境中启用 CGI::Carp 的 fatalsToBrowser 选项。它可以向攻击者透露有关脚本内部工作原理的详细信息。
两个参数open()
和全局文件句柄
两个参数open()
, e.g.
open FH, ">$file"
has a 许多与之相关的安全风险 https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=76775519当允许用户指定文件路径时。您的脚本通过使用硬编码的目录前缀来缓解其中的许多问题,但这绝不会削弱使用两个参数 open 可以非常危险。一般来说,您应该使用三参数形式:
open my $fh, ">", $file
(如果允许用户指定文件名,这仍然很危险;请参阅下面的“允许用户设置文件名”)。
另请注意,而不是全局文件句柄FH
我切换到词法文件句柄$fh
。请参阅 CERT 的页面不要使用裸字文件句柄 https://www.securecoding.cert.org/confluence/display/perl/FIO00-PL.+Do+not+use+bareword+file+handles由于某些原因。
不检查文件是否存在
您不检查文件是否已存在于/home/Desktop/$name
当你打开它进行书写时。如果文件已经存在,您将截断它(擦除其内容)一旦open()
调用成功,即使您从未向文件写入任何内容。用户(恶意的和其他的)可能会破坏彼此的文件,这不会让用户群感到非常高兴。
文件大小没有限制
“但是等等,”你说,“我设定了MAX_FILE_SIZE
在我的 HTML 表单中!” 请注意,这只是对浏览器的建议;攻击者可以轻松编辑 HTTP 请求以消除这种情况。切勿依赖隐藏的 HTML 字段来确保安全。隐藏字段在页面的 HTML 源代码和原始 HTTP 请求中清晰可见。您必须限制最大请求大小服务器端防止用户将大量文件加载到您的服务器并帮助减轻一种类型的拒绝服务攻击。设置$CGI::POST_MAX
变量位于 CGI 脚本的开头,如下所示:
$CGI::POST_MAX=1024 * 30; # 30KB
或者更好的是,在您的系统上找到 CGI.pm 并更改$POST_MAX
为所有使用 CGI 模块的脚本进行全局设置。这样您就不必记住在编写的每个 CGI 脚本的开头设置变量。
CGI 与 HTML 表单不匹配
用于 HTML 表单中文件路径的 POST 变量,userfile
,与您在 CGI 脚本中查找的变量不匹配,file
。这就是您的脚本失败并出现错误的原因
Is a directory
的价值
$cgi->param('file')
is undef
所以你的脚本尝试打开路径
/home/Desktop/
作为常规文件。
处理上传的过时方法
您正在使用旧的(并且过时的)方法来处理 CGI.pm 的上传,其中param()
用于获取文件名和轻量级文件句柄。这不适用于严格的并且不安全。这upload()
方法在 v2.47 中添加(早在 1999 年!)作为首选替代方法。像这样使用它(直接从CGI.pm 的文档 http://perldoc.perl.org/CGI.html#PROCESSING-A-FILE-UPLOAD-FIELD):
$lightweight_fh = $q->upload('field_name');
# undef may be returned if it's not a valid file handle
if (defined $lightweight_fh) {
# Upgrade the handle to one compatible with IO::Handle:
my $io_handle = $lightweight_fh->handle;
open (OUTFILE,'>>','/usr/local/web/users/feedback');
while ($bytesread = $io_handle->read($buffer,1024)) {
print OUTFILE $buffer;
}
}
where field_name
是保存文件名的 POST 变量的名称(在您的情况下,userfile
)。请注意,示例代码不会根据用户输入设置输出文件名,这引出了我的下一点。
允许用户设置文件名
Never允许用户选择将在您的服务器上使用的文件名。如果攻击者可以将恶意文件上传到已知位置,他们就会更容易利用它。相反,生成一个新的、唯一的(以防止破坏)、难以猜测的文件名,最好位于 Web 根目录之外的路径中,这样用户就无法直接使用 URL 访问它们。
其他事宜
你甚至还没有begun以解决以下问题。
验证
谁可以使用您的网络应用程序上传文件?您如何确保只有授权用户才能上传文件?
访问控制
用户是否可以看到其他用户上传的文件?根据文件内容,可能会存在重大隐私问题。
上传数量和速率
一名用户允许上传多少个文件?用户在固定时间内允许上传多少个文件?如果您不限制这些,即使您强制规定最大文件大小,一个用户也可能很快耗尽您的所有服务器资源。
危险文件类型
您将如何检查用户没有将危险内容(例如可执行的 PHP 代码)上传到您的服务器?仅仅检查文件扩展名或内容类型标头是不够的;攻击者已经找到了一些非常有创意的方法来规避此类检查。
“但是,但是,我只在公司内部网上运行这个......”
如果您的脚本无法从 Internet 访问,您可能会想忽略这些安全问题。但是,您仍然需要考虑
- 办公室里的恶作剧者
- 心怀不满的同事
- 需要访问您的应用程序或不应访问您的应用程序的协作者和外部承包商
- 经理们非常喜欢您的应用程序,以至于他们决定在您不知情的情况下(可能是在您调到另一个团队或离开公司之后)在互联网上向用户开放该应用程序
“我应该怎么办?”
废弃您现有的代码。仔细阅读我在第一段中列出的资源。他们又来了:
- perlsec http://perldoc.perl.org/perlsec.html
- the CERT Perl 安全编码标准 https://www.securecoding.cert.org/confluence/display/perl/CERT+Perl+Secure+Coding+Standard(特别是关于输入验证和数据
消毒 https://www.securecoding.cert.org/confluence/display/perl/01.+Input+Validation+and+Data+Sanitization)
- OWASP's 无限制文件上传 https://www.owasp.org/index.php/Unrestricted_File_Upload
- 信息安全的完整的文件上传漏洞 http://resources.infosecinstitute.com/file-upload-vulnerabilities/
- CWE's 危险类型文件无限制上传 http://cwe.mitre.org/data/definitions/434.html
- SANS 建议实现安全文件上传的 8 条基本规则 http://software-security.sans.org/blog/2009/12/28/8-basic-rules-to-implement-secure-file-uploads
仔细考虑一下,如果您really需要这样做。如果您只需要为用户提供存储文件的位置,请考虑使用 (S)FTP。这当然不会消除所有安全风险,但它会消除一个很大的风险:您的自定义 CGI 代码。
如果经过仔细考虑后您仍然认为这是必要的,请采取一些措施recentPerl 教程 http://perl-tutorial.org/#index3h1确保您可以使用并理解现代 Perl 编程约定。使用类似的框架代替 CGI.pmCatalyst http://www.catalystframework.org/, Dancer http://perldancer.org/, or 莫乔利西斯 http://mojolicio.us/,所有这些都有可以处理用户身份验证和会话等棘手领域的插件,因此您不必重新发明轮子(很糟糕)。
遵循上述资源中列出的所有安全准则,并考虑寻求网络安全专家的帮助。请小心行事:代码中的一个错误可能会让攻击者危及您的整个站点,甚至可能危及网络上的其他计算机。根据您的公司和用户所在的国家/地区,这甚至可能会产生法律后果。
肥皂盒>