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字節都應該足夠大,但我不得不承認這個值更像是「互聯網知識」。 –