在 PHP 中防止目录遍历但允许路径
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4205141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Preventing Directory Traversal in PHP but allowing paths
提问by Johnny
I have a base path /whatever/foo/
我有一个基本路径 /whatever/foo/
and
$_GET['path']
should be relative to it.
并且
$_GET['path']
应该是相对的。
However how do I accomplish this (reading the directory), without allowing directory traversal?
但是,如何在不允许目录遍历的情况下完成此操作(读取目录)?
eg.
例如。
/\.\.|\.\./
Will not filter properly.
不会正确过滤。
回答by ircmaxell
Well, one option would be to compare the real paths:
好吧,一种选择是比较真实路径:
$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);
$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);
if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
//Directory Traversal!
} else {
//Good path!
}
Basically, realpath()
will resolve the provided path to an actual hard physical path (resolving symlinks, ..
, .
, /
, //
, etc)... So if the real user path does not start with the real base path, it is trying to do a traversal. Note that the output of realpath
will nothave any "virtual directories" such as .
or ..
...
基本上,realpath()
将提供的路径解析为实际的硬物理路径(解析符号链接、..
、.
、/
、//
等)...因此,如果实际用户路径不是以实际基本路径开头,则它正在尝试进行遍历。请注意,输出realpath
将不会有任何“虚拟目录”,如.
或..
...
回答by Juan Pedro González
ircmaxell's answer wasn't fully correct. I've seen that solution in several snippets but it has a bug which is related to the output of realpath()
. The realpath()
function removes the trailing directory separator, so imagine two contiguous directories such as:
ircmaxell 的回答并不完全正确。我已经在几个片段中看到了该解决方案,但它有一个与realpath()
. 该realpath()
函数删除尾随目录分隔符,因此想象两个连续的目录,例如:
/foo/bar/baz/
/foo/bar/baz_baz/
As realpath()
would remove the last directory separator, your method would return "good path" if $_GET['path']
was equal to "../baz_baz" as it would be something like
与realpath()
删除最后一个目录分隔符一样,如果$_GET['path']
等于“../baz_baz” ,您的方法将返回“good path”,因为它类似于
strpos("/foo/bar/baz_baz", "/foo/bar/baz")
Maybe:
也许:
$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);
$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);
if ($realUserPath === false || strcmp($realUserPath, $realBase) !== 0 || strpos($realUserPath, $realBase . DIRECTORY_SEPARATOR) !== 0) {
//Directory Traversal!
} else {
//Good path!
}
回答by Cowlby
It is not sufficient to check for patterns like ../ or the likes. Take "../" for instance which URI encodes to "%2e%2e%2f". If your pattern check happens before a decode, you would miss this traversal attempt. There are some other tricks hackers can do to circumvent a pattern checker especially when using encoded strings.
仅检查 ../ 之类的模式是不够的。以“../”为例,哪个 URI 编码为“%2e%2e%2f”。如果您的模式检查发生在解码之前,您将错过这次遍历尝试。黑客可以采取其他一些技巧来绕过模式检查器,尤其是在使用编码字符串时。
I've had the most success stopping these by canonicalizing any path string to its absolute path using something like realpath() as ircmaxwell suggests. Only then do I begin checking for traversal attacks by matching them against a base path I've predefined.
我通过使用像 ircmaxwell 建议的 realpath() 之类的东西将任何路径字符串规范化为其绝对路径,从而取得了最大的成功。只有这样我才开始通过将它们与我预定义的基本路径进行匹配来检查遍历攻击。
回答by L4m0r
You may be tempted to try and use regex to remove all ../s but there are some nice functions built into PHP that will do a much better job:
您可能会尝试使用正则表达式来删除所有 ../s ,但 PHP 内置了一些不错的函数,它们可以做得更好:
$page = basename(realpath($_GET));
basename - strips out all directory information from the path e.g. ../pages/about.php
would become about.php
basename - 从路径中删除所有目录信息,例如../pages/about.php
将变为about.php
realpath - returns a full path to the file e.g. about.php
would become /home/www/pages/about.php
, but only if the file exists.
realpath - 返回文件的完整路径,例如about.php
将变为/home/www/pages/about.php
,但前提是文件存在。
Combined they return just the files name but only if the file exists.
组合它们仅返回文件名,但仅当文件存在时才返回。
回答by Lo Vega
1
1
put a null index.htm for -Index block
为 -Index 块放置一个空 index.htm
2
2
filter sQS on start
开始时过滤 sQS
// Path Traversal Attack
if( strpos($_SERVER["QUERY_STRING"], "../") ){
exit("P.T.A. B-(");
}
回答by sbnc.eu
When looking into the creation of new files or folders, I've figured I can use a two stage approach:
在研究新文件或文件夹的创建时,我认为我可以使用两阶段方法:
First check for traversal attempts using a custom implementation of a realpath()
like function, which however works for arbitrary paths, not just existing files. There's a good starting point here. Extend it with urldecode()
and whatever else you think may worth checking.
首先使用realpath()
类似函数的自定义实现检查遍历尝试,但是它适用于任意路径,而不仅仅是现有文件。有出发点的好位置。扩展它urldecode()
以及您认为值得检查的任何其他内容。
Now using this crude method you can filter out some traversal attempts, but it may be possible that you miss some hackish combination of special characters, symlinks, escaping sequences etc. But since you know for sure the target file does not exist (check using file_exists
) noone can overwrite anything. The worst case scenario would be that someone can get your code creating a file or folder somewhere, which may be an acceptable risk in most cases, provided your code does not allow them to write into that file/folder straight away.
现在使用这种粗略的方法你可以过滤掉一些遍历尝试,但你可能会错过一些特殊字符、符号链接、转义序列等的hackish组合。但是因为你确定目标文件不存在(检查使用file_exists
)没有人可以覆盖任何东西。最糟糕的情况是有人可以让您的代码在某处创建文件或文件夹,这在大多数情况下可能是可接受的风险,前提是您的代码不允许他们立即写入该文件/文件夹。
Finally so the path now points to an existing location, therefore you can now do the proper check using the methods suggested above utilising realpath()
. If at this point it turns out a traversal has happened, you are still safe more or less, as long as you make sure to prevent any attempts writing into the target path. Also right now you can delete the target file/dir and say it was a traversal attempt.
最后,路径现在指向现有位置,因此您现在可以使用上面建议的方法进行正确的检查realpath()
。如果此时发现发生了遍历,只要您确保防止任何写入目标路径的尝试,您或多或少仍然是安全的。现在您还可以删除目标文件/目录并说这是一次遍历尝试。
I'm not saying it cannot be hacked, since after all still it may allow illegitimate changes to be done to the FS, but still better than only doing custom checks, that cannot utilise realpath()
, and the window for abuse left open by making a temporary and empty file or folder somewhere is lower, than allowing them to make it permanent and even write into it, as it would happen with only a custom check that may miss some edge cases.
我并不是说它不能被黑客入侵,因为毕竟它仍然可能允许对 FS 进行非法更改,但仍然比仅进行无法利用的自定义检查要好realpath()
,并且通过临时创建滥用窗口并且某处的空文件或文件夹低于允许他们将其永久化甚至写入其中,因为仅在可能会错过某些边缘情况的自定义检查中就会发生这种情况。
Also correct me if I'm wrong pls!
如果我错了,请纠正我!
回答by J V
I assume you mean without allowing usersto traverse the directory yes?
我假设您的意思是不允许用户遍历目录是吗?
If you are trying to stop your own PHP from traversing the directory you should just make the php work properly in the first place.
如果你试图阻止你自己的 PHP 遍历目录,你应该首先让 php 正常工作。
What you need to stop users is a modified .htaccess file...
您需要阻止用户的是修改后的 .htaccess 文件...
Options -Indexes
(This all assumes you are talking about users)
(这一切都假设您在谈论用户)