Python Requests 图片下载 MIME 类型验证

3

我使用Python中的requests库通过HTTP下载大量的图片文件。我使用BytesIO将接收到的内容转换为原始字节,然后使用Pillow()将这些原始内容保存为JPEG文件。

from PIL import Image
from io import BytesIO

rsp = requests.get(imageurl)
content_type_received = rsp.headers['Content-Type'] # mime type
binarycontent = BytesIO(rsp.content)
if content_type_received.startswith('image'): # image/jpeg, image/png etc
    i = Image.open(binarycontent)
    outfilename = os.path.join(outfolder,'myimg'+'.jpg')
    with open(outfilename, 'wb') as f:
        f.write(rsp.content)
    rsp.close()

这段代码存在哪些潜在的安全风险?(我不确定我们能够相信服务器响应头中声明的 MIME 类型是否真的是服务器所说的类型)。有没有更好的方式来编写安全的下载程序?
1个回答

6
你的代码存在潜在的安全风险,这取决于你对所连接的服务器的信任程度。如果你确信该服务器不会使用恶意内容欺骗你,那么使用该代码片段相对较安全。否则,请自行检查内容类型。最大的潜在风险可能是无意中保存了一个可执行文件而不是图像。更小的风险可能是存储了一种不同类型的内容,可能会导致PIL或应用程序中的另一个组件崩溃。
请记住,服务器可以选择任何响应头的值,包括内容类型。如果你有理由怀疑所连接的服务器可能不诚实,你就不应该信任请求头。
如果你想要一种更可靠的方法来确定所接收到的内容的内容类型,我建议你查看python-magic,这是libmagic的一个封装库。这个库将帮助你自己确定内容类型,因此你不必“信任”你从中下载的服务器。
# ...
content = BytesIO(rsp.content)
mime = magic.from_buffer(content.read(1024), mime=True)
if mime.startswith('image'):
    content.seek(0) # Reset the bytes stream position because you read from it
    # ...

python-magic文档非常详细,如果您考虑使用它,我建议您查看他们的README。


很好的答案。在我接受它之前,为什么只读取了来自响应内容的1024字节?因为这足以从中推断出图像的MIME类型吗?只是出于好奇,如果请求的是另一种类型的媒体,比如MP4,那么会如何确定呢? - hAcKnRoCk
1
要准确评估MIME类型所需读取的适当字节数很难确定,因为它在很大程度上取决于您正在读取的文件类型。有些文件签名甚至会将此信息放置在偏移量中。1024字节应该足够处理任何类型的图像,但我必须承认这个值相当“互联网知识”式的。 - Alvae

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接