1

我使用python中的请求库通过http下载大量图像文件。我使用Python中的BytesIO将接收到的内容转换为原始字节,然后使用Pillow()将此原始内容保存为jpeg文件。图像下载MIME类型验证python请求

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

代码的潜在安全风险取决于您信任服务器的多少。 如果你确定服务器永远不会试图欺骗你一些恶意内容,那么你使用这段代码相对安全。 否则,请自行检查内容类型。 最大的潜在风险可能是在不知不觉中保存可执行文件而不是图像。 较小的可能是存储可能会导致应用程序中的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,如果你考虑用户它。

+0

不错的答案。在我接受它之前,为什么只从响应内容读取1024个字节?因为足以从中推断出图像的MIME类型?出于好奇,如何确定所要求的是另一种类型的媒体,比如说mp4? – hAcKnRoCk

+1

为了准确评估mimetype,您必须阅读的字节数量很难知道,因为它取决于您正在阅读的文件类型。一些文件签名甚至将这些信息与偏移量相加。对于任何类型的图像类型,1024字节都应该足够大,但我不得不承认这个值更像是“互联网知识”。 –