问题描述
我有一个URL,它将为用户提供受保护的文件。
上载文件时,无论其名称如何,文件名都由我的应用程序重写,并存储在数据库中。所以我知道它永远不会包含 /或 ..。
文件名是: USER_ID _ RANDOMMD5。FILE_EXT
USER_ID =当前登录的用户ID和 RANDOM MD5。
ie 5_ji3uc237uckj92d0jf3932t09ut93f2.pdf
这是我提供文件的功能:
功能user_file($ file_name =)
{
if($ file_name)
{
//确保没有有趣的公司名称:
$ file_name = str_replace( '..','',$ file_name);
$ file_name = str_replace( /,,$ file_name);
//检查是否允许用户访问该文件
$ user_id = substr($ file_name,0,strpos($ file_name, _));
//现在执行逻辑以检查用户是否已登录,并且用户是文件
的所有者(($ this-> ion_auth-> logged_in())&& ((($ user_id === $ this-> user-> id))
{
//通过readfile()服务文件
}
}
}
问题:这是确保没有问题的安全方法吗?
edit 1 :ion_auth是我的身份验证库,而 $ this-> user-> id是存储在我的结构中的用户ID
编辑2:用户文件存储在public_html外部-并且因此,只能通过我的应用程序AFAIK进行访问
编辑3:我的改进代码使用了以下Amber的想法,并考虑了我需要的事实适应不同的文件扩展名,我将尝试避免数据库命中:
函数user_files($ file_name =)
{
//出于安全原因,请检查文件名是否正确
//检查32位md5值,后跟单个。,然后是3-4个扩展名(仅z),
if(preg_match('^ [A-Za-z0-9] {32} + [。] {1} [A-Za-z] {3,4} $ ^',$ file_name))
{
//现在检查用户已登录
if($ this-> ion_auth-> logged_in())
{
//将请求重写到服务器上的路径-并将user_id附加到文件名
/ /这可以确保用户只能访问自己上传的文件
//由于在上传过程中将userid附加到文件名中!
$ file = MY_SECURE_FOLDER。$ this->用户-> id 。’_’。$ file_name;
//现在检查文件是否存在
if(file_exists($ file))
{
//提供文件
标头('Content-类型:。get_mime_by_extension($ file));
readfile($ file);
}
}
}
}
更好的主意:让用户提供MD5,然后自己构造文件名。这样,您就不必对用户输入和文件名进行各种疯狂的检查-您只需确保MD5是40个字符的字符串,其字符串是 [0-9a-f] ,那么您就很好。
I have a URL which will serve a protected file to my user.
The file name is re-written by my application when the file is uploaded, regardless of the name, and stored in my database. So I know it will never contain a "/" or ".."
The filename is: "USER_ID"_"RANDOMMD5".FILE_EXTWith "USER_ID" = current logged in user ID and "RANDOM MD5" exactly that.
i.e. 5_ji3uc237uckj92d0jf3932t09ut93f2.pdf
This is my function to serve the file:
function user_file($file_name = "")
{
if ($file_name)
{
// Ensure no funny business names:
$file_name = str_replace ('..', '', $file_name);
$file_name = str_replace ('/', '', $file_name);
// Check if the user is allowed to access this file
$user_id = substr($file_name, 0, strpos($file_name, "_"));
// now do the logic to check user is logged in, and user is owner of file
(($this->ion_auth->logged_in()) && (($user_id === $this->user->id))
{
// Serve file via readfile()
}
}
}
Question: Is this a secure way to ensure that there is no other way for the person to transverse directories, gain access to other files etc?
edit 1: ion_auth is my authentication library, and "$this->user->id" is the user id stored in my construct
edit 2: The user files are stored outside public_html - and thus only accessible via my application AFAIK
edit 3: My improved code, using the ideas from Amber below, taking into account the fact I need to accomodate for different file extensions, and I'm going to try and avoid a database hit:
function user_files($file_name = "")
{
// for security reasons, check the filename is correct
// This checks for a 32bit md5 value, followed by a single "." followed by a 3-4 extension (of only a-z)
if (preg_match('^[A-Za-z0-9]{32}+[.]{1}[A-Za-z]{3,4}$^', $file_name))
{
// Now check the user is logged in
if ($this->ion_auth->logged_in())
{
// Rewrite the request to the path on my server - and append the user_id onto the filename
// This ensures that users can only ever access their own file which they uploaded
// As their userid was appended to the filename during the upload!
$file = MY_SECURE_FOLDER.$this->user->id.'_'.$file_name;
// Now check if file exists
if (file_exists($file))
{
// Serve the file
header('Content-Type: '.get_mime_by_extension($file));
readfile($file);
}
}
}
}
解决方案 Better idea: have the user supply the MD5, and construct the filename yourself. That way you don't have to do all sorts of crazy checking for user input versus filenames - you can just ensure that the MD5 is a 40-character string of [0-9a-f]
only, and then you're good.
这篇关于此PHP函数是否可以防止文件遍历?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!