如何使用 ImageMagick 防止图像炸弹?

How to prevent image bombs with ImageMagick?

我目前在 PHP 上使用 Imagick 库并使用 Image Magick 的调整大小功能。我刚刚了解了减压炸弹以及 ImageMagick 如何容易受到它的攻击。

我已经检查了如何在不实际将图像加载到 memory/disk 的情况下 ping 图像并验证图像的尺寸。限制 ImageMagick 的内存和磁盘限制也更安全,这样它就不会在磁盘上写入一个巨大的文件。

我已经阅读并可以使用 setResourceLimit() 来完成。 http://php.net/manual/en/imagick.setresourcelimit.php

IMagick::setResourceLimit(IMagick::RESOURCETYPE_MEMORY , 100);
IMagick::setResourceLimit(IMagick::RESOURCETYPE_DISK , 100);

$thumb = new Imagick('image.png');
$thumb->resizeImage(320,240,Imagick::FILTER_LANCZOS,1);

然而,设置磁盘和内存限制后,如果图像确实达到了这个限制,我得到的只是一个分段错误错误,不会抛出任何异常。这让我无法妥善处理。

更新:

以下是我使用的软件包版本:

dpkg -l | grep magick
ii  imagemagick-common                    8:6.6.9.7-5ubuntu3.3              image manipulation programs -- infrastructure
ii  libmagickcore4                        8:6.6.9.7-5ubuntu3.3              low-level image manipulation library
ii  libmagickwand4                        8:6.6.9.7-5ubuntu3.3              image manipulation library
ii  php5-imagick                          3.1.0~rc1-1                       ImageMagick module for php5

all I get is a segmentation fault error, no exceptions are thrown

我猜您的段错误是由于资源设置得太低,ImageMagick(和相关代表)无法运行。资源的值以字节为单位,而不是兆字节。

Imagick 确实 在到达资源时抛出异常。通常像...

"cache resource exhausted"

减压炸弹,或 Zip-Bombs,极难识别。您通过 ping-ing 图像和设置资源限制所做的事情是正确的做法。我会粗略地概述一个解决方案...

// Define limits in application settings, or bootstrap (not dynamically!)
define('MY_MAGICK_MEMORY_LIMIT', 5e+8);
// Repeat for AREA, DISK, & etc.

// In application
$image = new Imagick(); // allocate IM structrues
// Set limits on instance
$image->setResourceLimit(Imagick::RESOURCETYPE_MEMORY, MY_MEMORY_LIMIT);
// Repeat for RESOURCETYPE_AREA, RESOURCETYPE_DISK, & etc.

$filename = 'input.png';
if($image->ping($filename)) {
    // Validate that this image is what your expecting
    // ...
    try {
       $image->read($filename); // <-- Bomb will explode here
       $image->resizeImage(320,240,Imagick::FILTER_LANCZOS,1);
    } catch( ImageickException $err ) {
       // Handle error
    }
}
unset($image)

如果您不信任解压缩,您可以在 ping 验证期间利用 Imagick::getImageCompression 来检查图像需要哪种压缩。压缩类型将是一个整数,它将映射到以下枚举...

typedef enum
{
  UndefinedCompression,
  B44ACompression,
  B44Compression,
  BZipCompression,
  DXT1Compression,
  DXT3Compression,
  DXT5Compression,
  FaxCompression,
  Group4Compression,
  JBIG1Compression,
  JBIG2Compression,
  JPEG2000Compression,
  JPEGCompression,
  LosslessJPEGCompression,
  LZMACompression,
  LZWCompression,
  NoCompression,
  PizCompression,
  Pxr24Compression,
  RLECompression,
  ZipCompression,
  ZipSCompression
} CompressionType;

MagickStudio(用 PERL 编写)为 default resource limits, and how they are checked against 上传的图像提供了一个很好的起点(搜索 Ping。)

从 ImageMagick-6.9.0-1 开始,"width" 和 "height" 添加了资源限制。在命令行中,使用“-limit width 32000”等。如果宽度或高度超过指定限制,ImageMagick 的 PNG 解码器将在不解压缩图像的情况下退出。

PNG 解码器不会尝试解压缩宽度或高度超过限制的图像。

"area" 资源在早期版本的 ImageMagick(和 Imagick)中可用;但是 PNG 解码器不会拒绝基于 "area" 限制的图像(请参阅 Danack 的评论)。

在ImageMagick 6.9.0之前的版本中,宽度和高度限制来自libpng,并取决于libpng版本。当前的 libpng 版本(1.0.16 及更高版本、1.2.6 及更高版本、1.5.22 及更高版本以及 1.6.17 及更高版本)施加了 1,000,000 列和宽度限制。在版本 1.2.0 到 1.2.5、1.5.0 到 1.5.23 和 1.6.0 到 1.6.16 中,默认限制为 27 亿行和列。

在 Imagick 中查找 RESOURCETYPE_AREA(我在您引用的手册中没有看到 _WIDTH 或 _HEIGHT,因此需要更新 Imagick 或其手册)。所以试试

IMagick::setResourceLimit(IMagick::RESOURCETYPE_AREA , 100M);

设置 100 兆像素限制。希望 Imagick 的某些未来版本将支持 RESOURCETYPE_WIDTH 和 RESOURCETYPE_HEIGHT,为解压炸弹漏洞提供更好的解决方案。请参阅 Danack 关于使用当前版本的 IMagick 设置这些的回答。

设置 'Resource Area' 限制仅设置图像不保存在内存中的大小,而是分页到磁盘。如果您想使用该设置来实际限制可以打开的最大尺寸图像,您还需要设置 'Resource Disk' 限制。

下面的代码正确地给出了拍摄的图像炸弹的内存分配错误 from here

try {
    Imagick::setResourceLimit(Imagick::RESOURCETYPE_AREA, 2000 * 2000);
    Imagick::setResourceLimit(Imagick::RESOURCETYPE_DISK, 2000 * 2000);

    $imagick = new Imagick("./picture-100M-6000x6000.png");
    $imagick->modulateImage(100, 50, 120);
    $imagick->writeImage("./output.png");

    echo "Complete";
}
catch(\Exception $e) {
    echo "Exception: ".$e->getMessage()."\n";
}

输出为:

Exception: Memory allocation failed `./picture-100M-6000x6000.png' @ error/png.c/MagickPNGErrorHandler/1630

如果你想设置宽高资源,并且ImageMagick版本>=6.9.0-1你应该可以直接使用WidthResource = 9, HeightResource = 10的值

//Set max image width of 2000
Imagick::setResourceLimit(9, 2000);
//Set max image height of 1000
Imagick::setResourceLimit(10, 1000);

这些不必以编程方式设置,您可以通过随 ImageMagick 安装的 policy.xml 文件来设置它们。如果在程序中设置了 none,ImageMagick 会读取该文件并使用这些设置 - 这可能是一种更方便的设置方式,因为您可以在每台机器上更改它们。

This makes it impossible for me to handle it properly.

这让你无法在同一个进程中处​​理它。您可以通过 运行 在后台任务中处理图像来很好地处理它。

我个人认为,无论如何,在由网络浏览器直接访问的服务器中使用 Imagick 是疯狂的。 运行 它作为后台任务(由 http://supervisord.org/ 之类的东西管理)并通过需要处理的作业队列与该后台任务通信要安全得多。

这不仅解决了 'bad images can bring down my website' 问题,还可以更轻松地监控资源使用情况,或者将图像处理转移到 CPU 比 Web 前端更快的机器上终端服务器需求。

来源 - 我是 Imagick 扩展的维护者,我最近将其添加到 Imagick 自述文件中:

Security

The PHP extension Imagick works by calling the ImageMagick library. Although the ImageMagick developers take good care in avoiding bugs it is inevitable that some bugs will be present in the code. ImageMagick also uses a lot of third party libraries to open, read and manipulate files. The writers of these libraries also take care when writing their code. However everyone makes mistakes and there will inevitably be some bugs present.

Because ImageMagick is used to process images it is feasibly possible for hackers to create images that contain invalid data to attempt to exploit these bugs. Because of this we recommend the following:

1) Do not run Imagick in a server that is directly accessible from outside your network. It is better to either use it as a background task using something like SupervisorD or to run it in a separate server that is not directly access on the internet.

Doing this will make it difficult for hackers to exploit a bug, even if one should exist in the libraries that ImageMagick is using.

2) Run it as a very low privileged process. As much as possible the files and system resources accessible to the PHP script that Imagick is being called from should be locked down.

3) Check the result of the image processing is a valid image file before displaying it to the user. In the extremely unlikely event that a hacker is able to pipe arbitrary files to the output of Imagick, checking that it is an image file, and not the source code of your application that is being sent, is a sensible precaution.