2014-02-26 23 views
12

几年前,我贴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会自动将它转换为../

这段代码有什么问题?什么可能是允许目录遍历或以某种方式破坏某些东西的输入值?

+8

'/ etc/passwd'仍然被接受 – SLaks

+4

@SLaks:URI http://example.com?path=/etc/passwd会给出类似'/ home/demo-site/etc/passwd的结果',所以不,这不是问题。 –

+1

好问题。虽然它真的很痒,但我还是无法找到一个错误,而且它真的讨厌'/ etc/passwd'的建议,它使得所有的upvotes都在这里......而错误的是。安全性很好,但是因为..因为它不是很酷,所以把FUD放在了附近。我仍然不满意这个代码(为什么没有类型检查?限制它到某个白名单等),但事实是所有这个膝盖跳动也是不好的 – Nanne

回答

3

我刚刚通过Burp intruder跑你的代码并不能找到在这种情况下,任何捷径可走的。

这可能是向下投由于对雇用了类似的做法被列入黑名单的某些字符组合,其他/旧技术堆栈漏洞。正如你所提到的,当前版本的PHP自动对URL进行解码输入,但是存在诸如双重URL编码(点= %252e),16位Unicode编码(点= %u002e),超长UTF-8 Unicode编码(点= %c0%2e)或插入一个空字节(%00)可以诱使过滤器,并允许一旦被给予一个大拇指由滤波器服务器端代码来解释路径作为未编码的版本。

这就是为什么它已经设置警钟响了。即使你的方法似乎在这里工作,通常情况可能并非如此。技术总是在变化,总是最好谨慎小心,并尽可能使用对字符集解释免疫的技术,例如使用已知好字符的白名单,这些字符可能总是好的,或者使用文件系统函数(在链接答案中提到了realpath)以验证实际路径是您期望的路径。

5

有很多,可以漏网之鱼其他的可能性,如:

.htaccess 
some-secret-file-with-a-password-in-it.php 

换句话说,在目录或子目录什么是访问,包括.htaccess文件和源代码。如果该目录或其子目录中的任何内容不能下载,那么这就是一个安全漏洞。

+4

所有这三个路径都会导致基本目录内的文件。鉴于我所引用的问题/答案的背景,我不认为这是一个安全问题。 –

+1

我没有看到问题(或链接的问题和答案)中的任何内容,表明目录中的所有文件都可以下载。如果我错过了什么,请告诉我。但这是一个潜在的安全漏洞,所以它是对这个问题的有效答案。 –

+0

@Nanne我修改了我的答案,它实际上说明了这一点。 –

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; 
3

我想不出任何情况下,在此应该失败。

不过,我不知道如何PHP的file_exists在内部实现,是否有一些目前尚不清楚的怪癖。就像PHP had null byte related issues with some file system functions until PHP 5.3.4。因此,为了安全起见,我宁愿检查已经解决的路径,而不是盲目地信任PHP并且 - 可能更重要的是 - 我的假设,四个提到的序列是唯一可以产生路径的路径高于指定的基本目录。这就是为什么我宁愿ircmaxell’s solutionyours