dirname是否足以防止目录遍历攻击?

4
考虑以下经典问题案例:
<?php
$filename = "/tmp/".$_GET['f'];
readfile($filename);

这段代码存在目录遍历攻击漏洞,例如如果$_GET['f']的值为../etc/shadow,攻击者将会看到该文件的内容。
有已知的方法来防止这种类型的攻击,我不会问如何做到。问题是:dirname的以下用法是否是防御攻击的万无一失之举?
<?php
if (dirname($_GET['f']) != '.') die ('Attack prevented');

听起来应该是由于dirname的原因:

  • 仅当输入路径中没有斜杠时(在线文档提供了不太严格的保证,但源代码是明确的)才返回.
  • 是二进制安全的(因此无法被嵌入的空值欺骗)

因此,据我所知,唯一可能的攻击途径就是以某种编码将数据传递给$_GET['f'],使得字符/\(别忘了Windows)编码为不包含相应字符ASCII值的内容,并且同时这种编码必须被底层C运行时库的fopen函数透明地支持。

不允许使用非ASCII值,这就排除了所有单字节编码、UTF-8和UTF-16的两种格式;此外,由于C运行时规范是平台无关的,攻击只能适用于某些使用“易受攻击”的编码来表示名称的文件系统。据我所知,这样的文件系统不存在;即使存在,也几乎没有任何人会创建它;最后,即使它存在,PHP也不会在这种假想的异国情调系统上托管。

总之,我认为这个检查是100%安全的。我有什么遗漏吗?


我使用了这个答案中的代码,你也可以查看那个问题中的MainMa答案。 - tttony
@tttony:谢谢,但正如我上面所说,我知道如何防止攻击。问题是,这种方式能否可靠地预防? - Jon
根据我的测试,你的方法可行,看起来简单但确实有效。 - tttony
1个回答

1

我不确定我会声称某件事情是100%安全的。尽管如此,我无法想到一个明显的情况,这种情况会不安全,我已经尝试了很多变化。尽管如此,您需要添加一个检查,确保 $_GET['f'] 不为空。如果访问上述代码的页面没有为 f 提供值,则会显示“攻击已阻止”消息,这可能不是期望的效果。

<?php
if (!empty($_GET['f']) && dirname($_GET['f']) != '.') die ('Attack prevented');

当然,这些示例包含最少的代码以进行说明。你尝试了哪种排列组合? - Jon
我创建了一个文件,将其放在上一级目录,并尝试了各种引用该文件的方式(例如?f=../test.txt、?f=..\test.txt、?f=..%2Ftest.txt、?f=/test.txt、?f=%2Ftest.txt、?f=asdf等)。只有真正本地于脚本的文件路径没有抛出错误。再次声明,“100%安全”是不恰当的,但我无法找到一些明显的方法来解决这个问题。 - Chris Rasco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接