几年前,我发布了an answer关于PHP的一种方式的问题,让用户在URI中传递要下载的文件的相对路径,同时防止目录遍历。
我得到了一些评论,说明代码是不安全的,还有一些downvotes(最近的是今天)。这是代码:
$path = $_GET['path'];
if (strpos($path, '../') !== false ||
strpos($path, "..\\") !== false ||
strpos($path, '/..') !== false ||
strpos($path, '\..') !== false)
{
// Strange things happening.
}
else
{
// The request is probably safe.
if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . $path))
{
// Send the file.
}
else
{
// Handle the case where the file doesn't exist.
}
}
我一遍又一遍地检查了代码,对其进行了测试,但仍然无法理解它引入的安全问题。
我在评论中得到的唯一提示是../
可以替换为%2e%2e%2f
。这不是问题,因为PHP会自动将其转换为../
。
这段代码有什么问题?什么可能是允许目录遍历或以某种方式破坏某些内容的输入值?
答案 0 :(得分:5)
还有许多其他可能性可以通过,例如:
.htaccess
some-secret-file-with-a-password-in-it.php
换句话说,目录或子目录中的任何内容都是可访问的,包括.htaccess
个文件和源代码。如果该目录或其子目录中的任何内容不应该是可下载的,那么这就是一个安全漏洞。
答案 1 :(得分:3)
我想不出任何会失败的情况。
但是,我不知道PHP的file_exists
是如何在内部实现的,以及它是否有一些当前未知的怪癖。就像PHP had null byte related issues with some file system functions until PHP 5.3.4。
所以为了安全起见,我宁愿检查已经解决的路径,而不是盲目地信任PHP,而且 - 可能更重要 - 我的假设,提到的四个序列是唯一可以产生路径的路径。在指定的基目录之上。这就是为什么我更喜欢ircmaxell’s solution到yours。
答案 2 :(得分:3)
我刚刚通过Burp intruder运行了代码,在这种情况下找不到任何方法。
由于对其他/旧技术堆栈的攻击,它可能会被投票,因为通过将某些字符组合列入黑名单采用了类似的方法。
正如您所提到的,当前版本的PHP会自动对URL进行解码输入,但是存在一些缺陷,例如双URL编码(dot = %252e
),16位Unicode编码(dot = {{1} }),过长的UTF-8 Unicode编码(dot = %u002e
)或插入空字节(%c0%2e
)可能会欺骗过滤器并允许服务器端代码将路径解释为未编码的版本过滤器竖起了大拇指。
这就是为什么它已经敲响了警钟。即使您的方法似乎在这里工作,通常情况可能并非如此。技术总是在变化,最好是谨慎行事,并尽可能使用对字符集解释不敏感的技术,例如使用可能总是好的已知良好字符的白名单,或使用文件系统功能(链接的答案中提到realpath)以验证实际路径是您期望的路径。
答案 3 :(得分:2)
黑名单是一个坏习惯。你最好使用白名单(在允许的文字字符串或允许的字符上)。
if(preg_match('/^[A-Za-z0-9\-\_]*$/', $path) ) {
// Yay
} else {
// No
}
或者:
switch($path) {
case 'page1':
case 'page2':
// ...
break;
default:
$path = 'page1';
break;
}
include $path;