删除 Python 图像中的潜在恶意软件

Removing potential malware in image in Python

我了解到 .PNG、.JPEG 和其他图像文件类型可能包含恶意软件。

我希望删除用户上传图片中嵌入的潜在恶意软件。在 Python 中有没有一种方法可以从本质上 "flatten" 删除任何恶意内容的图像?有点像如果您要截取图像然后保存屏幕截图?或者可能有一种图像类型不容易损坏?

我已经将所有用户上传的内容托管在一个单独的域中,但我想知道我是否可以更进一步。

"malicious" 内容包含在图像元数据中。

当您读取矩阵中的图像数据(像素、颜色)时(例如使用 python 中的 pillow 又名 PIL 库),您只需获取图像数据。

当你保存回来时,元数据丢失,只保留图像数据。

不过,将其保存回去可能会产生不良副作用:

  • 有损压缩会改变图像
  • 根据输出格式,透明度可能不受支持并丢失

在最简单的层面上,位图图像包含两件事:

  • 元数据,即有关图像的信息,以及

  • 像素数据,即像素颜色本身。

元数据包含重要的内容,例如图像的高度和宽度、通道数、每像素位数、图像的色彩空间及其压缩方式。它还包含可以说不太重要的补充信息,例如:

  • EXIF 数据 - 使用的相机、镜头、曝光度、GPS 信息等
  • 用于精确色彩再现的 ICC 颜色配置文件
  • IPTC 信息 - 新闻和电信信息、版权、主题标记等
  • 地理参考 and/or 摄影测量信息 - 请参阅 GeoTIFF
  • 评论 - 可以包含任意信息(和恶意软件)

像素数据包含构成图像的像素网格的颜色(可能还有任何透明度)。它经常被压缩。

请注意,以上是简单的级别。我只提到了位图文件而没有提到矢量文件,例如 SVG 文件,它们可能包含自己的一系列问题,例如 "Billion Laughs DoS Attack" 请参阅 https://en.wikipedia.org/wiki/Billion_laughs_attack

另请注意,完全可以将整个可执行程序附加到图像的末尾或中间,而不必打乱图像 readers/display 程序,这些程序通常会忽略它们无法忽略的信息'不懂但尽量使用他们所做的部分。如果你想要一个例子,这里我用 ImageMagick 制作了一个红色图像,并将 128kB 的任意数据附加到末尾,并将其显示在 Mac 的终端中,没有任何投诉macOS:

magick -size 1024x768 xc:red image.png             # make red image
dd if=/dev/zero bs=128 count=1024 >> image.png     # append 128kB of whatever I like - not actually malware in this case
open imge.png                                      # use "xdg-open" on Linux

另请注意,可以使用隐写术嵌入其他信息,例如,劫持每个像素的最低有效位并使用它来传达消息或携带一些意想不到的有效负载,例如恶意软件或水印。由于它是最低有效位,因此通常视觉上难以察觉。


所以,现在的问题是你希望做出什么样的权衡,或者换句话说"how paranoid are you?"你决定从图像中剥离的信息越多,就越多您可能会无意中丢失一些您以后需要的信息。如果剥离 EXIF 数据,您将不再知道图像是何时、何地或由谁拍摄的。如果您剥离 ICC 颜色配置文件,您的图像可能会在某些查看器中显得褪色、过饱和或绿色。如果您剥离 IPTC 信息,如果合同要求您保留这些信息,您可能会侵犯许可。如果您剥离地理参考信息,您的数据可能会变得无用。如果删除注释,您可能会丢失屏蔽信息、版权或标记信息。如果将格式从 PNG/TIFF/GIF 更改为 JPEG,您将失去透明度和准确性。如果您从 TIFF 更改为 PNG,您将失去存储 32 位、64 位或浮点数据以及超过 4 个通道的能力。如果您从 JPEG 更改为 PNG,您可能会不经意地使文件变大数十或数百倍。

因此,您可能采取的最偏执的行动几乎就是将位图加载到内存中,以无法存储任何其他像素的格式保存(出于性能原因,最好在内存中而不是磁盘中)数据(例如 PPM 或原始 RGB(A) 字节)并将其重新保存为 JPEG 或 PNG。这将丢弃所有 EXIF/IPTC/Geo-data 和注释,以及任何附加在图像末尾或中间的无关数据。如果你想要一个具体的例子,你可以在终端中使用以下 ImageMagick 命令:

magick input.jpg -strip ppm:- | magick ppm:- result.jpg

如果您使用 PIL/Pillow 和 Python,您可以:

from PIL import Image
import numpy as np

# Load image
im = Image.open('image.jpg')                                     

# Convert to format that cannot store IPTC/EXIF or comments, i.e. Numpy array
na = np.array(im)                                                                       

# Create new image from the Numpy array and save
result = Image.fromarray(na).save('clean.jpg')

如果您的图像是 PNG 格式,那么您会增加复杂性 - 它可能是调色板图像并且可能包含 alpha/transparency 信息,您可能希望保留该信息。这可能看起来像这样:

from PIL import Image
import numpy as np

# Load image
im = Image.open('image.png')                                     

# Convert to format that cannot store IPTC/EXIF or comments, i.e. Numpy array
na = np.array(im)                                                                       

# Create new image from the Numpy array
result = Image.fromarray(na)

# Copy forward the palette, if any
palette = im.getpalette()
if palette != None:
    result.putpalette(palette)

# Save result
result.save('clean.png')

如果您需要保留一些元数据,则需要考虑其他选项。