【问题标题】:Do I need to filter the $_FILES['file'] before dealing with it? [closed]在处理它之前我需要过滤 $_FILES['file'] 吗? [关闭]
【发布时间】:2020-10-24 17:52:44
【问题描述】:

为了保护我们正在编程的网站免受 SQL 注入或 XSS 等攻击,我们需要在存储或显示之前过滤用户的输入。

在 PHP 中,我们对输入使用 htmlspecialcharsaddslashes 函数来防止 XSS 和 SQL 注入攻击。那么,文件呢?

我曾经通过检查文件类型和扩展名来保护网络应用程序,以了解这些文件是否在白名单中。但是我没有使用htmlspecialcharsaddslashes 函数,因为我没有看到有人使用这种方法。

例如,如果我想获取我使用$_FILES['file']['tmp_name'] 的文件名,那么我将它直接存储到数据库中。

这是错误的还是不能注入代码、命令...等

【问题讨论】:

  • In PHP, we use htmlspecialchars and addslashes functions to the inputs to prevent XSS and SQL-Injection attacks .... 这还不够。 PHP确实有一些你应该研究的内置过滤选项。
  • 这是一个关于过滤$_FILES 元素的好问题。我不知道。
  • $_FILES['file']['tmp_name'] 是服务器上临时文件的名称,它不是从客户端机器传递到服务器的文件的名称。此文件没有由点和一些字母数字字符(例如.pdf 等)指示的文件类型。
  • @Martin 你是对的,但是 $_FILES 是一个包含一些变量的数组(不仅仅是'tmp_name')。例如,您有变量“name”,它是来自机器的文件名,也可以注入!
  • 也请不要在您的问题中包含答案。如果您有答案要提供,请将其作为答案发布在下方,并且不要将其他人的答案汇总为您自己的答案。这不是 Stack Overflow 的工作方式。

标签: php file-upload protection htmlspecialchars addslashes


【解决方案1】:

我需要在处理 $_FILES['file'] 之前对其进行过滤吗?

简短回答:不。就是一堆字符串值,仅此而已。

长答案:

我曾经通过检查文件类型和扩展名来保护网络应用程序,以了解这些文件是否在白名单中。

如果应用和执行正确,这是一个很好的方法。

$_FILES 数组只是一个载体。它本身不能被滥用,但你必须信任它携带的东西 - 即信任正在传递给/由服务器传递的文件。


当我写下这个答案时;以下;似乎 OP 对他们实际保护的内容以及原因感到困惑:

OP 声明为“最佳实践”(绝对不是):

如果您想使用 $_FILES['file']['tmp_name'] 存储到您的数据库中或显示在您的 UI 中,您应该使用 addlashes 或 PDO 准备语句来保护免受 SQL 注入攻击。

这是对$_FILES 数组的填充方式的误解。 $_FILES['file']['tmp_name']由服务器设置,而不是由用户或客户端设置。

用户给定的值为:

$_FILES['file']['name']
$_FILES['file']['type']
$_FILES['file']['size']

这些是需要审查的字符串值。只要您不信任这些字符串值,您就不必担心。


在您的数据库中存储文件是not usually a good idea 并且有它自己的陷阱,dhnwebpro has their own answer 在这个问题上,关于数据库安全。


$_FILES['file']['tmp_name']是文件在临时存储空间中的服务器位置。

PHP Manual 明确指出:

默认情况下,文件将存储在服务器的默认临时目录中,除非在 php.ini 中使用 upload_tmp_dir 指令指定了另一个位置。可以通过在 PHP 运行的环境中设置环境变量 TMPDIR 来更改服务器的默认目录。

如果文件没有被移走或重命名,则该文件将在请求结束时从临时目录中删除。

如果您认为您的 $_FILES['file']['tmp_name'] 值被滥用,那么这是服务器受损的迹象,并且您遇到了一大堆麻烦,远远超出了恶意文件上传。 em>


那么,如何审核正在携带的文件?

有多种类型的文件攻击,这个主题远远超出了您所询问的范围。例如;真正的 JPEG 图像可以在 JPEG 元数据中包含 XSS 脚本,但是在加载和查看 JPEG 时会触发此 XSS,但出于所有意图和目的,JPEG 文件不是“坏文件”或不是 XSS 文件,以外部观察者不专门检查此漏洞

那么,您是屏蔽这个file.jpg 还是屏蔽所有 Jpeg 文件?这是一个艰难的决定,但在 PHP 中有一些非常好的解决方法(我相信这也超出了这个问题的范围)。简而言之;您的问题可以通过一些编辑和明确来说明您要保护的具体内容以及您愿意走多远才能达到该保护级别。

我可以为您提供一个粗略的全面指南,以防止某些 MIME 文件类型被您的服务器接受。这看起来和感觉就像您想要的那样,可以阻止偷偷摸摸的 MP4 视频作为文档文件上传(反之亦然)。

1:

忽略文件名 ($_FILES['file']['name'])。 从不信任用户数据。

编辑:正如meagar 所指出的,您可能需要保留原始文件名,在这种情况下,您应该使用正则表达式或类似方法检查它以删除不需要的字符... p>

2:

忽略声明的文件类型 ($_FILES['file']['type'])。任何给定 MIME 类型的文件名(例如.pdf)都应该被忽略。 从不信任用户数据。

3:

使用 PHP Finfo 函数集作为初步指标。它完美,但能捕捉到大多数东西。

$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
$whitelist = ['text/html','image/gif','application/vnd.ms-excel'];
finfo_close($finfo);
if(in_array($mimeType,$whitelist)){
    // File type is acceptable.
}

4:图片:

如果您要检查上传的图片,最好的方法是按照 3 检查 finfo 文件类型,然后让 PHP 将图片加载到空白画布中并重新保存图片,从而剥离所有多余的元数据和其他可能不受欢迎的不是图像数据的数据。

Like this method: Remove exif data from jpg using php.

5:

建议您始终为您上传的文件随机命名,切勿使用$_FILES['file']['name'] 值。

6:

根据您要避免和/或消除的威胁类型,您可以打开上传的文件并读取文件的前几个字节,并将其与该类型白名单文件中确认的字节进行比较。这是非常细微的,再次超出了这个答案的范围,这已经足够长了。

【讨论】:

  • 所有合理的建议,但并不总是可以忽略用户提供的文件名,具体取决于您的 UX。在许多情况下,您希望为用户保留原始文件名。
  • @meagar 是的,那么应该使用正则表达式或类似的方法进行检查。我会编辑。干杯
【解决方案2】:

有一个函数is_uploaded_file 可以确定该文件确实是一个上传的文件,而不是用户的某种文件路径操作。据我所知,is_uploaded_file($_FILES['file']['tmp_name']) 不可能返回 false。您还应该检查filesize($_FILES['file']['tmp_name']) 是否小于您要插入的列的大小。

至于“将其直接存储到数据库”,您仍然需要在文件内容上使用practice good SQL injection prevention。此外,通常很难扩展将文件存储在数据库中的解决方案,但这是您可能已经考虑过的一个单独问题。

【讨论】:

  • $_FILES['file']['size'] 可以被客户端传递,不应该被信任。在处理 blob 数据时,此值也可能不可靠,因为 exact 文件大小值可能取决于正在读取文件的操作系统。如果文件已经上传到服务器,最好读取服务器上静止文件的大小,而不是依赖于第三手信息....
  • 谢谢,我已经相应地编辑了我的回复。
【解决方案3】:

如果您使用的是 PDO 或 MySQLi,您应该能够将文件放在准备好的语句中,这样可以保护您免受 SQL 注入攻击。我从https://www.mysqltutorial.org/php-mysql-blob/ 粘贴了一个方法,其中包含一些关于在 MySQL 数据库中存储文件的有用信息。

/**
 * insert blob into the files table
 * @param string $filePath
 * @param string $mime mimetype
 * @return bool
 */
public function insertBlob($filePath, $mime) {
    $blob = fopen($filePath, 'rb');

    $sql = "INSERT INTO files(mime,data) VALUES(:mime,:data)";
    $stmt = $this->pdo->prepare($sql);

    $stmt->bindParam(':mime', $mime);
    $stmt->bindParam(':data', $blob, PDO::PARAM_LOB);

    return $stmt->execute();
}

或者您可以将文件存储在文件系统中,并在需要提供文件时仅包含对该文件的引用。这种方法速度更快,但不方便将所有数据保存在一个地方。

关于 $_FILES 数组元素的详细信息在手册中有点隐藏,但可以在示例 1 的末尾找到它们:

https://www.php.net/manual/en/features.file-upload.post-method.php

$_FILES 数组的所有元素的值都应视为用户输入。我建议忽略这些值。但是,如果您希望将它们写入数据库和/或稍后在您的 UI 中显示它们,您肯定需要保护自己免受 SQL 注入和 XSS 攻击。因此,在这种情况下,使用准备好的语句和 htmlspecialchars 不会有什么坏处。

【讨论】:

  • 感谢您的回复,但我不是在谈论如何准备参数以传递给查询以存储在数据库中以及如何保护它以进行 SQL 注入。我的问题是我是否需要preparehtmlspecialchars $_FILES 数组的变量,然后再存储它?
  • 如果您只是担心$_FILES 的变量而不是文件本身,那么准备好的语句将保护您免受SQL 注入。我通常会获得自己的文件名和 mime 类型值,因此我不会将它们视为来自用户或其浏览器的值。因此,如果您不打算自己推出自己的产品,那么在这些产品上使用 htmlspecialchars 可能不会有什么坏处,因为我认为它们可能包含 XSS 代码。
  • 是的,你是对的。解决方案是同时准备和使用htmlspecialchars 来保护该阵列免受SQL-Injection 和XSS 等攻击。请将这些详细信息添加到您的答案中,以将问题设置为已回答。
  • 我注意到您发布的方法没有关于如何显示文件的真实 MIME 类型的指南.....:-/
  • htmlspecialchars 用于使任意字符串安全地显示在 Web 浏览器中。它与数据库无关。您应该为上下文转义数据,而不是为将来的每一个潜在用途而抢先转义它。在您数据库读取数据并将其发送到浏览器之前,请勿使用htmlspecialchars
猜你喜欢
  • 2021-08-11
  • 2019-09-05
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 2021-04-20
  • 2020-05-05
相关资源
最近更新 更多