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

Do I need to filter the $_FILES['file'] before dealing with it?

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

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

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

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

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

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

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

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

Do I need to filter the $_FILES['file'] before dealing with it?

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

长答案:

I used to protect web-apps by checking the files type and it's extension to know if those were in the whitelist or not.

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

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


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

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

If you want to use $_FILES['file']['tmp_name'] to be stored into your database or to display in your UI, you should use addslashes or PDO prepare statement to be protected against SQL-Injection attacks.

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

用户给定的值为:

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

这些是需要审查的字符串值。只要您不信任这些字符串值,就没有什么可担心的。


在数据库中存储文件 not usually a good idea and has its own pitfalls, 关于数据库安全的问题。


$_FILES['file']['tmp_name'] 是临时存储中文件的服务器位置 space.

PHP Manual 明确指出:

Files will, by default be stored in the server's default temporary directory, unless another location has been given with the upload_tmp_dir directive in php.ini. The server's default directory can be changed by setting the environment variable TMPDIR in the environment in which PHP runs.

The file will be deleted from the temporary directory at the end of the request if it has not been moved away or renamed.

如果您认为您的 $_FILES['file']['tmp_name'] 值被滥用,那么这是服务器妥协的迹象,您的盘子上有一大堆麻烦,远远超出了恶意文件上传。


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

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

那么,您是阻止这个 file.jpg 还是阻止 所有 Jpeg 文件?这是一个艰难的决定,但在 PHP 中有一些非常好的解决方法(我认为这也超出了这个问题的范围)。简而言之;您的问题可以通过一些编辑和清晰度来说明您到底要防止什么以及您愿意在多大程度上达到该保护级别。

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

1:

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

编辑:正如 所指出的,您可能需要保留原始文件名,在这种情况下,您应该使用 REGEX 或类似工具检查它以删除不需要的字符...

2:

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

3:

使用PHPFinfo函数集作为初步指标。它完美,但可以捕获大部分内容。

$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 将图像加载到空白 canvas 并重新保存图像,从而去除所有多余的元数据和其他非图像数据的潜在不良数据。

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

5:

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

6:

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