我有一个表单,用户填写歌词信息并上传专辑封面。提交的数据将被插入数据库,专辑封面将被移动到子文件夹。
localhost/project-folder/covers
我已经采取了一些预防措施(转义,预处理语句)对SQL注入的表单输入。最近,我了解到我上传文件(图片)也需要注意,用户可以上传恶意图片。
例如,在图像元数据中添加HTML、JS或PHP代码,或者直接将代码嵌入图像文件中。因为我没有广泛使用PHP,所以我不知道这会带来什么问题,尤其是在我的情况下。
我在服务器端做表单验证。
localhost/项目文件夹/歌词/. php
<form action="../scripts/lyrics/submit_lyrics.php" id="lyricsForm" method="post" autocomplete="off" enctype="multipart/form-data">
<我> localhost/项目文件夹/脚本/歌词/submit_lyrics.php 我>
$form_data = new FormData(["artist", "album", "song", "year", "track_no", "lyrics"], "sssiis");
$file_data = new FileUpload("cover", [
"max_file_size" => 512 * 1024,
"extensions" => ["gif", "jpg", "jpeg", "png"],
"mimes" => ["image/gif", "image/jpeg", "image/png"],
"max_width" => 1024,
"max_height" => 1024,
]);
$cover = new Cover($mysqli, $form_data, $file_data, BASE."covers/");
验证在FormData
和FileUpload
初始化时完成。如果有一个无效的字段或上传的图像是无效的,用户将被重定向到表单页面(add.php),并给出相应的警告。
我读过的防止恶意图像上传的方法之一是从上传的图像中创建一个新图像,这就是我在new Cover()
中所做的。我还调整了上传的图像的大小,以便这种方法有效。我用这个函数来调整大小:
public function new_image($file_data, $new_width, $new_height) {
$img_data = file_get_contents($file_data->tmp_name);
$image_type = $file_data->type;
$img_create = null;
switch ($image_type) {
case IMAGETYPE_GIF:
$img_create = "imagecreatefromgif";
break;
case IMAGETYPE_JPEG:
$img_create = "imagecreatefromjpeg";
break;
case IMAGETYPE_PNG:
$img_create = "imagecreatefrompng";
break;
}
$uploaded_image_resource = $img_create($file_data->tmp_name);
$new_image_resource = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($new_image_resource, $uploaded_image_resource, 0, 0, 0, 0, $new_width, $new_height, $file_data->image["width"], $file_data->image["height"]);
return $new_image_resource;
}
public function write_to_disk() {
if (isset($this->image["resource"])) {
$destination = $this->target_dir . $this->file_name . ".jpg";
imagejpeg($this->image["resource"], $destination);
imagedestroy($this->image["resource"]);
}
}
这个调整大小也删除(我认为)元数据中的任何代码和/或嵌入在图像中的代码(如果有的话),因为我正在创建一个新的干净的图像。
文件上传保护是否足够?我错过什么了吗?还有什么我需要注意的吗?
例如,在图像元数据中添加HTML、JS或PHP代码,或者直接将代码嵌入图像文件中。因为我没有广泛使用PHP,所以我不知道这是如何产生问题的
原则上不应该这样做:如果您将图像以正确的媒体类型(如image/jpeg
)返回给最终用户,则应该仅将其视为图像并呈现。
但是,周围有一些工具可以忽略该类型信息,并将内容视为更危险的类型:
-
旧的浏览器,特别是IE,会嗅探文件的内容来猜测它可能是什么类型,并且在文件内容中包含HTML标记会导致它将其呈现为HTML而不是图像。用户提供的HTML =跨站点脚本。
-
插件;历史上,Java会将第三方站点嵌入的任何资源视为applet,而另一个站点上的Flash插件可以对文件使用
loadPolicyFile
将内容重新解释为跨域。xml策略,从而打开跨站点脚本 -
现在的情况并没有那么糟糕,因为这些问题已经通过各种方式得到了缓解——例如,在松鼠的例子中,它作为text/html提供,只是被重新解释为image/jpeg,这是一种不那么强大的类型(向下嗅探)。然而,我们并没有明确承诺文件不会被当前或未来的工具在web平台上以不同的类型重新使用。
这个调整大小也删除(我认为)元数据中的任何代码和/或嵌入在图像中的代码(如果有的话),因为我正在创建一个新的干净的图像。
它肯定会删除元数据;实际上只有imagecreatefromX
然后imagejpeg
会这样做,因为PHP图像对象不保留元数据。
但是它不一定会改变图像本身的内容。理论上,知道您正在使用的图像压缩器的攻击者可以构造一个图像,当该图像被该代码压缩时,输出一个攻击者选择的字节串,这可能会像上面那样被不安全地误解。
这可能是攻击吗?不。我想我可能可以把它与简单的无损压缩器(如GIF或PNG)相比较,但是更复杂、更不可预测的JPEG有损压缩可能会使它更棘手,更耗时。
我希望图像加载和保存舞蹈可以保护您免受绝大多数随机攻击者的攻击(当然还有其他很好的理由,例如确保特定的图像大小和格式),但它并不是针对XSS上传攻击的安全保证。
如果你需要更好的,或者你需要允许上传其他任意的文件类型,你不能重新映像压缩他们,最严重的网站采取的方法是提供用户上传的内容,只从一个单独的主机名(*),这样如果任何类型的XSS攻击成功了,你的主站不会受到影响。
(*:理想情况下,在一些额外的费用,甚至一个单独的域名。无论哪种方式,用户内容主机名都不能是主站的子域,否则它可能能够从中读取会话cookie并以这种方式危及它。