【问题标题】:Prevent image upload code injection防止图片上传代码注入
【发布时间】:2016-12-20 20:52:39
【问题描述】:

我有一个表单,用户填写歌词信息并上传专辑封面。提交的数据将被插入数据库,专辑封面将被移动到子文件夹。

localhost/project-folder/covers

我已经针对表单输入的 SQL 注入采取了一些预防措施(转义、准备好的语句)。最近了解到,文件(图片)上传也需要注意,用户可以上传恶意图片。

例如,在图像元数据中添加 HTML、JS 或 PHP 代码,或 embedding code directly into the image file。由于我没有广泛使用 PHP,所以我不知道这会带来什么问题,尤其是在我的情况下。

我正在服务器端进行表单验证。

localhost/project-folder/lyrics/add.php

<form action="../scripts/lyrics/submit_lyrics.php" id="lyricsForm" method="post" autocomplete="off" enctype="multipart/form-data">

localhost/project-folder/scripts/lyrics/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/");

验证在FormDataFileUpload 的初始化时完成。如果存在无效字段或上传的图像无效,用户将被重定向回表单页面(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"]);
    }
}

此调整大小还会删除(我认为)元数据中的任何代码和/或图像中嵌入的代码(如果有的话),因为我正在创建一个新的干净图像。

这足以保护文件上传吗?我错过了什么吗?还有其他我需要注意的事情吗?

【问题讨论】:

    标签: php html upload xss image-uploading


    【解决方案1】:

    例如,在图像元数据中添加 HTML、JS 或 PHP 代码,或将代码直接嵌入到图像文件中。由于我没有广泛使用 PHP,我不知道这是如何造成问题的

    原则上不应该这样:如果您使用正确的媒体类型(例如 image/jpeg)将图像提供给最终用户,则它应该仅作为图像处理和呈现。

    但是,有些工具会忽略该类型信息并将内容视为更危险的类型:

    • 旧的浏览器,尤其是IE,会嗅探文件的内容以猜测它可能是什么类型,并且在文件内容中包含 HTML 标记会导致将其呈现为 HTML 而不是图片。用户提供的 HTML = 跨站点脚本。

    • 插件;从历史上看,Java 会将第三方站点嵌入的任何资源视为小程序,而另一个站点上的 Flash 插件可以对文件使用 loadPolicyFile 以将内容重新解释为 crossdomain.xml 策略,从而打开跨站点脚本

    • 今天的情况并没有那么糟糕,因为这些问题已经通过各种方式得到缓解 - 例如在松鼠示例中,它用作 text/html 并且仅被重新解释为图像/jpeg,这是一种功能较弱的类型(低吸)。但是,我们并没有明确承诺不会通过当前或未来的工具在网络平台上将文件作为不同类型重复使用。

    此调整大小还会删除(我认为)元数据中的任何代码和/或图像中嵌入的代码(如果有的话),因为我正在创建一个新的干净图像。

    它肯定会删除元数据;实际上只是 imagecreatefromX 然后 imagejpeg 会这样做,因为 PHP 图像对象不保留元数据。

    但它不一定会改变图像本身的内容。从理论上讲,知道您正在使用哪种图像压缩器的攻击者可以构造一张图片,当该图片被该代码压缩时,会输出一串攻击者选择的字节,这可能会像上面那样被不安全地误解。

    这是一次可能的攻击吗?不。我想我可能可以将其与 GIF 或 PNG 之类的简单无损压缩器相比较,但 JPEG 中更复杂、更不可预测的有损压缩可能会使它变得更加棘手和耗时。

    我希望图像加载和保存舞蹈可以保护您免受绝大多数临时攻击者的侵害(当然还有其他充分的理由,例如确保特定的图像大小和格式),但这并不难和快速的 XSS 上传攻击安全保障。

    如果您需要比这更好的,或者您需要允许上传其他无法重新图像压缩的任意文件类型,大多数严肃网站采用的方法是仅从单独的文件中提供用户上传的内容hostname(*),这样如果任何类型的 XSS 攻击成功,您的主站点都不会受到影响。

    (*: 理想情况下,即使是单独的域名,也要付出更多的代价。无论哪种方式,用户内容的主机名都不能是主站点的子域,否则它可能能够从中读取会话 cookie 并破坏它那样。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      • 2010-12-20
      • 2020-04-16
      • 2011-10-18
      • 2022-08-05
      • 2012-12-09
      • 2016-12-03
      相关资源
      最近更新 更多