1

私はpythonでリクエストライブラリを使用して、http経由で多数のイメージファイルをダウンロードします。 PythonでBytesIOを使用して受信したコンテンツを生のバイトに変換し、この生のコンテンツをjpegファイルとして保存するためにPillow()を使用します。イメージダウンロード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タイプを推論するだけで十分ですか?好奇心の外に、要求されたものが別のタイプのメディアである場合、これはどのように決定されますか? – hAcKnRoCk

+1

mimetypeを正確に評価するために読まなければならない適切なバイト数は、読んでいるファイルのタイプによって大きく異なるため、分かりづらいです。一部のファイルシグネチャは、この情報をオフセットとともに配置します。どのような種類の画像タイプでも十分な1024バイトが必要ですが、私はその価値が「インターネット知識」のようなものであることを認めなければなりません。 –