4

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

我已经检查了如何在不实际将其加载到内存/磁盘中的情况下 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
4

3 回答 3

6

设置“资源区域”限制仅设置图像不保存在内存中的大小,而是分页到磁盘。如果您想使用该设置来实际限制可以打开的最大图像大小,您还需要设置“资源磁盘”限制。

下面的代码正确地给出了从这里拍摄的图像炸弹的内存分配错误。

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";
}

输出是:

异常:内存分配失败`./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文件来设置它们。如果程序中没有设置任何设置,ImageMagick 会读取该文件并使用这些设置——这可能是一种更方便的设置方式,因为您可以在每台机器上更改它们。

这使我无法正确处理它。

它使您无法在同一进程中处理它。您可以通过在后台任务中运行图像处理来处理它。

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

这不仅解决了“糟糕的图像会导致我的网站瘫痪”的问题,而且还可以更轻松地监控资源使用情况,或者将图像处理转移到 CPU 比 Web 前端服务器需要的更快的机器上。

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

安全

PHP 扩展 Imagick 通过调用 ImageMagick 库来工作。尽管 ImageMagick 开发人员非常注意避免错误,但代码中不可避免地会出现一些错误。ImageMagick 还使用许多第三方库来打开、读取和操作文件。这些库的作者在编写代码时也很小心。但是,每个人都会犯错误,并且不可避免地会出现一些错误。

由于 ImageMagick 用于处理图像,因此黑客可能会创建包含无效数据的图像以试图利用这些漏洞。因此,我们建议以下内容:

1) 不要在可以从网络外部直接访问的服务器上运行 Imagick。最好将其用作使用 SupervisorD 之类的后台任务,或者在无法直接在 Internet 上访问的单独服务器中运行它。

这样做会使黑客难以利用漏洞,即使 ImageMagick 正在使用的库中应该存在漏洞。

2)将其作为特权非常低的进程运行。应尽可能锁定调用 Imagick 的 PHP 脚本可访问的文件和系统资源。

3) 在显示给用户之前检查图像处理的结果是一个有效的图像文件。在极不可能的情况下,黑客能够将任意文件通过管道传输到 Imagick 的输出,检查它是图像文件,而不是正在发送的应用程序的源代码,是一种明智的预防措施。

于 2015-07-22T11:36:47.730 回答
3

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

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

“区域”资源在早期版本的 ImageMagick(和 Imagick)中可用;但是 PNG 解码器不会根据“区域”限制拒绝图像(请参阅 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);

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

于 2015-07-21T21:10:09.450 回答
1

我得到的只是一个分段错误错误,没有抛出异常

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

如果达到资源, Imagick抛出异常。通常像...

“缓存资源耗尽

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

// 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 编写)为默认资源限制提供了一个很好的起点,以及如何根据上传的图像检查它们(搜索Ping.)

于 2015-07-21T18:47:18.907 回答