我有一个名为gallery的php文件.php它是用户特定的图像库的页面。为了安全起见,用户图像存储在 Web 根目录之外。
为了检索每个用户的相应文件,我使用了一个 getimage.php 文件,该文件从他们的位置提供图像。总而言之,目录结构如下所示:
- 用户图片
- 用户1
- 用户 1 的图像列表
- 用户 2
- 用户 2 的图像列表
- 用户1
- public_html
- 图库.php
- 获取图像.php
getimage.php 编写如下:
$imgString = realpath('/UserImages/' . $_SESSION['username'] . '/' . $_GET['img']);
if (!startsWith($imgString, '/UserImages/' . $_SESSION['username'] . '/')
|| !(endsWith(strtolower($imgString), '.jpg') || endsWith(strtolower($imgString), '.jpeg')))
{
header('HTTP/1.0 403 Forbidden');
die();
}
$img = file_get_contents($imgString);
header("Content-type: image/jpeg");
echo($img);
exit();
我依靠 $_GET['img'] 来确定要检索的图像,这是一个可能的安全漏洞(也是一个主要漏洞(。我可以预见目录遍历攻击,因此使用 realpath,尽管我确信此脚本不包含其他攻击途径。
出于这个原因,我宁愿我可以将getimage.php移动到webroot之外,或者至少阻止它直接访问(并且只能通过画廊.php,其中发送的img参数严格在我的控制之下(。
然而,每当我尝试移动getimage.php public_html时,即使我做一个要求或包含在画廊.php中,我似乎也不能再调用它了。我访问getimage.php的方式是这样做的:
<img src=getimage.php?img=IMG_FILENAME.jpg />
但是getimage.php如果我将其移出public_html目录,它将失败。
所以,长话短说:我需要做些什么来防止getimage.php被滥用?
最好的安全方法是不要使用 $_GET['img'] 传递图像 URL,而是发送对图像的引用。
问题所在
首先,$_GET['img'] 可以是有害代码的任何外部 URL,file_get_contents您可以直接下载它。
其次,任何人都可以将您的服务器用作免费的图像代理,只需在自己的网站上使用以下 url:
<img src="http://www.yourwebsite.com/getimage.php?img=[external_image_url]">
正确的方法
1( 在数据库中创建包含用户图像路径的图像表。当您想要提供图像时,请传递图像 ID 而不是 URL:
$imageID = $_GET['image'];
.... your sql query here to retrieve the image....
$img = file_get_contents($imgString);
2(因为你知道$_GET['image']是一个ID,所以你可以很容易地清理它:
if( !preg_match("/^[0-9]+$/", $_GET['image']) )
{
header('HTTP/1.1 404 Not Found');
exit();
}
3(在你的SQL中不要忘记使用预准备语句:
$sql = "SELECT image_path from images WHERE id = ?";
4(只有图像应该移动到文档根文件夹之外,如果你想移动getimage.php在根目录之外,你也可以使用Apache别名来提供它。在 Apache Virtualhost 配置中,您可以使用:
Alias /getimage.php /path/to/getimage.php
5(使用404未找到而不是403禁止,不要给攻击者一个提示,你抓住了他的行动。
您需要允许 Apache(假设(使用 Directory 指令 (http://httpd.apache.org/docs/2.2/mod/core.html#directory( 访问带有图像的文件夹。
或者,如果您想保护图像,您可以将图像移动到根目录下并使用 .htaccess 限制对它们的直接访问。
使用目录结构执行此操作的唯一方法是清理 $_GET['img'] 变量并确保它没有任何不需要的字符。
您可以使用正则表达式来执行此操作,该表达式过滤除字母,数字,下划线,连字符和点之外的所有内容。
尝试将代码的第一行替换为此...
$imgString = realpath('/UserImages/' . $_SESSION['username'] . '/' . preg_replace("/[^a-zA-Z0-9._-]/", "", $_GET['img']));
或者,您可以在代码之前使用此简单的 If 语句检查 $_GET['img'] 变量是否包含错误字符。
if(preg_replace("/[^a-zA-Z0-9._-]/", "", $_GET['img']) != $_GET['img']){
// Throw an error
exit();
}
PS:不要忘记在阅读之前检查您的文件是否存在,否则如果没有,您将收到错误。您可以将文件存在用作额外的安全检查。