考虑以下经典问题案例:
<?php
$filename = "/tmp/".$_GET['f'];
readfile($filename);
此代码容易受到目录遍历攻击,例如,如果$_GET['f']
的值../etc/shadow
则该文件的内容将泄露给攻击者。
有众所周知的方法可以防止这种类型的攻击;我不是在问如何做到这一点。问题是:以下使用dirname
是防止攻击的防弹方式吗?
<?php
if (dirname($_GET['f']) != '.') die ('Attack prevented');
听起来应该是因为dirname
:
- 返回
.
当且仅当输入路径中没有斜杠时(在线文档做出了不太严格的保证,但源代码是明确的) - 是二进制安全的(因此不会被嵌入的 null 欺骗)
因此,据我所知,唯一可能的攻击途径是以编码方式将数据传递给$_GET['f']
,以便字符/
或(我们不要忘记Windows)编码为不包含相应字符的ASCII值的内容,同时这种编码必须由底层C运行时库的
fopen
函数透明地支持。
无 ASCII 值限制排除了所有单字节编码、UTF-8 和两种 UTF-16 版本;此外,由于 C 运行时的规范与平台无关,因此攻击只能适用于某些使用"易受攻击"编码来表示名称的文件系统。据我所知,这样的文件系统并不存在;任何人都很难创建它;最后,即使PHP存在,也不会托管在这样一个假设的外来系统上。
总之,在我看来,这张支票是 100% 安全的。我错过了什么吗?
我不确定我是否会声称某些东西是 100% 安全的。也就是说,我想不出一个明显的例子,这是不安全的,我尝试了大量的排列来反对它。也就是说,您需要添加一张支票,证明 $_GET['f'] 不为空。访问带有上述代码且没有 f 值的页面给了我"已阻止攻击"消息,这可能不是预期的效果。
<?php
if (!empty($_GET['f']) && dirname($_GET['f']) != '.') die ('Attack prevented');