0

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

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

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

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

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

4

3 回答 3

3

在处理它之前我需要过滤 $_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']

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


在数据库中存储文件通常不是一个好主意,并且有其自身的缺陷,dhnwebpro 对这个问题有自己的答案,关于数据库安全。


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

PHP手册明确指出:

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

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

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


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

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

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

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

1:

忽略文件名 ( $_FILES['file']['name'])。永远不要相信用户数据。

编辑:正如meagar所指出的,您可能需要保留原始文件名,在这种情况下,您应该使用 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:图片:

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

喜欢这种方法:使用 php 从 jpg 中删除 exif 数据

5:

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

6:

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

于 2020-07-05T21:25:48.083 回答
1

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

至于“将其直接存储到数据库”,您仍然需要对文件内容进行良好的 SQL 注入预防。此外,通常很难扩展将文件存储在数据库中的解决方案,但这是您可能已经考虑过的另一个问题。

于 2020-07-04T04:39:41.093 回答
-1

如果您使用 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不会造成伤害。

于 2020-07-04T04:28:40.930 回答