我有一个php脚本,它通过$_GET
获取相对路径名,读取该文件并创建它的缩略图。我不希望用户能够从服务器读取任何文件。只允许来自某个目录的文件,否则脚本应为exit()
。
这是我的文件夹结构:
files/ <-- all files from this folder are public
my_stuff/ <-- this is the folder of my script that reads the files
我的脚本可通过mydomain.com/my_stuff/script.php?pathname=files/some.jpg
访问。什么不应该被允许e。 g。:mydomain.com/my_stuff/script.php?pathname=files/../db_login.php
所以,这是my_stuff
文件夹中脚本的相关部分:
...
$pathname = $_GET['pathname'];
$pathname = realpath('../' . $_GET['pathname']);
if(strpos($pathname, '/files/') === false) exit('Error');
...
我对这种方法不太确定,对我来说似乎不太安全。谁有更好的主意?
答案 0 :(得分:3)
我先做realpath()
(解析任何“../”和其他引用),然后检查结果是否是允许的子目录。
采取你的场景(我不明白为什么你需要使用文件/在这种情况下,如果它是唯一允许的目录,但无论如何):
$basedir = "/etc/www";
$allowed = "/etc/www/files";
$pathname = realpath($basedir."/".$_GET["pathname"]);
if (!$pathname)
die ("Unknown file path");
// Check whether $pathname begins with $allowed (= is a sub-directory)
if (substring($pathname, 0, strlen($allowed)) != $allowed)
die ("illegal access!");
据我所知,这应该是一种安全的方法。
答案 1 :(得分:2)
这个怎么样(它处理移动服务器的情况,因为没有硬编码的绝对路径):
$pathname = realpath('../' . $_GET['pathname']);
$rootpath = realpath('../files/');
if (strpos($pathname, $rootpath) !== 0) exit('Error');
答案 2 :(得分:0)
我曾经这样做过一次 basedir被定义为脚本所在的目录。
$basedir=dirname(__FILE__)."/";
$systemdir=realpath($_SERVER['DOCUMENT_ROOT'].$dir)."/";
if (substr($systemdir,0,strlen($basedir)) !== $basedir) {
......但是佩卡更快:)
答案 3 :(得分:0)
我还会检查./
中是否存在../
或$_GET['pathname']
,因为这样做几乎是一个他们不应该去的地方。确实realpath
使其安全(因为..
和.
已“解决”),但strpos
检查在某些(可能是错误的)情况下可能很弱/files/
字符串稍后出现但在访问实际文件时会被忽略,例如像
/site/my_stuf/secretfile.txt /files/
(使用这样的字符串来访问文件应该“看到”“secretfile.txt”(包含空格)不是dir名称,一切都应该失败......但正如所说,这个假设可能是真正的一个错误场景,如果技巧利用其他一些不那么假设的错误...当然不知道它被利用了吗?
所以我特别重复我的建议,检查GET字符串中的../
;并且,为什么不将此字符串解释为从files
开始的路径,即将files/
添加到用户提供的内容中,并避免已经说过../
的存在?
我不知道所有这些是否是偏执的(或更好的解决方案存在),但我对基于url的目录/文件列表器使用了类似的方法,它允许“直接”访问我的站点的子目录,我当然希望确保没有办法逃离该目录。
答案 4 :(得分:0)
NB。使用realpath提供的答案是可以的,但是他们应该提到realpath在所有服务器变体中都不起作用。