PHP - 访问 Web 根目录之外的文件的安全性


PHP - Security in accessing files outside of web root

我有一个名为gallery的php文件.php它是用户特定的图像库的页面。为了安全起见,用户图像存储在 Web 根目录之外。

为了检索每个用户的相应文件,我使用了一个 getimage.php 文件,该文件从他们的位置提供图像。总而言之,目录结构如下所示:

  • 用户图片
    • 用户1
      • 用户 1 的图像列表
    • 用户 2
      • 用户 2 的图像列表
  • 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:不要忘记在阅读之前检查您的文件是否存在,否则如果没有,您将收到错误。您可以将文件存在用作额外的安全检查。