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

时间:2021-11-05 08:28:24

Consider the following classic problem case:

考虑以下经典问题案例:

<?php
$filename = "/tmp/".$_GET['f'];
readfile($filename);

This code is vulnerable to a directory traversal attack, for example if the value of $_GET['f'] is ../etc/shadow the contents of that file will be disclosed to the attacker.

此代码容易受到目录遍历攻击,例如,如果$ _GET ['f']的值为../etc/shadow,该文件的内容将被泄露给攻击者。

There are well-known approaches to prevent this type of attack; I am not asking how to do that. The question is: is the following use of dirname a bulletproof way to prevent the attack?

有一些众所周知的方法来防止这种类型的攻击;我不是问怎么做。问题是:以下是使用dirname防弹攻击的防弹方法吗?

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

It sounds like it should be since dirname:

这听起来应该是因为dirname:

  • returns . if and only if there are no slashes in the input path (the online documentation makes a less rigorous guarantee but the source is explicit)
  • 回报。当且仅当输入路径中没有斜杠时(在线文档提供不太严格的保证,但源是明确的)

  • is binary-safe (so cannot be tricked by embedded nulls)
  • 是二进制安全的(因此不能被嵌入的空值欺骗)

So as far as I can tell, the only possible avenue of attack would be to pass data to $_GET['f'] in an encoding such that either the character / or \ (let's not forget Windows) encodes to something that does not contain the ASCII value of the corresponding character and at the same time this encoding has to be transparently supported by the underlying C runtime library's fopen function.

所以据我所知,唯一可能的攻击途径是将数据传递给$ _GET ['f'],编码使得字符/或\(让我们不要忘记Windows)编码为不能编码的东西。包含相应字符的ASCII值,同时这个编码必须由底层C运行时库的fopen函数透明地支持。

The no-ASCII-value restriction rules out all single-byte encodings, UTF-8, and both flavors of UTF-16; furthermore, since the spec for the C runtime is platform-agnostic the attack could only be applicable to some filesystem that used a "vulnerable" encoding to represent names. Such a filesystem does not, to my knowledge, exist; it would hardly make sense for anyone to create it; and finally PHP would not be hosted on such a hypothetical exotic system even if it existed.

无ASCII值限制排除了所有单字节编码,UTF-8和两种风格的UTF-16;此外,由于C运行时的规范与平台无关,因此攻击只适用于使用“易受攻击”的编码来表示名称的某些文件系统。据我所知,这样的文件系统不存在;任何人创造它都没有意义;最后PHP不会托管在这样一个假设的异域系统上,即使它存在。

In conclusion, it seems to me that this check is 100% safe. Is there something I missed?

总之,在我看来,这项检查是100%安全的。我错过了什么吗?

1 个解决方案

#1


1  

I'm not sure I'd ever make the claim that something is 100% safe. That said, I can't think of an obvious case where this would be unsafe and I tried a ton of permutations against it. That said, you'll want to add a check that $_GET['f'] isn't empty in there. Visiting a page with the above code with no value for f gave me the "Attack prevented" message, which is probably not the desired effect.

我不确定我是否会声称某些东西是100%安全的。也就是说,我不能想到一个明显的例子,这是不安全的,我尝试了大量的排列反对它。也就是说,你要添加一个检查,$ _GET ['f']在那里不是空的。使用上面的代码访问一个没有f值的页面给了我“防止攻击”的消息,这可能不是预期的效果。

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

#1


1  

I'm not sure I'd ever make the claim that something is 100% safe. That said, I can't think of an obvious case where this would be unsafe and I tried a ton of permutations against it. That said, you'll want to add a check that $_GET['f'] isn't empty in there. Visiting a page with the above code with no value for f gave me the "Attack prevented" message, which is probably not the desired effect.

我不确定我是否会声称某些东西是100%安全的。也就是说,我不能想到一个明显的例子,这是不安全的,我尝试了大量的排列反对它。也就是说,你要添加一个检查,$ _GET ['f']在那里不是空的。使用上面的代码访问一个没有f值的页面给了我“防止攻击”的消息,这可能不是预期的效果。

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