我有很多用戶上傳的內容,我想驗證上傳的圖像文件實際上不是惡意腳本。在Django文檔,它指出的ImageField:Django ImageField驗證(這是否足夠)?
「繼承的FileField所有屬性和方法,同時也驗證上傳對象是有效的圖像。」
那是完全準確?我讀過壓縮或以其他方式處理圖像文件是一個很好的驗證測試。我假設PIL做這樣的事情....
威爾的ImageField去朝着覆蓋我的圖片上傳安全很長的路要走?
我有很多用戶上傳的內容,我想驗證上傳的圖像文件實際上不是惡意腳本。在Django文檔,它指出的ImageField:Django ImageField驗證(這是否足夠)?
「繼承的FileField所有屬性和方法,同時也驗證上傳對象是有效的圖像。」
那是完全準確?我讀過壓縮或以其他方式處理圖像文件是一個很好的驗證測試。我假設PIL做這樣的事情....
威爾的ImageField去朝着覆蓋我的圖片上傳安全很長的路要走?
另一個測試是與file命令。它檢查文件中是否存在「幻數」以確定其類型。在我的系統上,file
包裝包括libmagic
以及基於ctypes的包裝/usr/lib64/python2.7/site-packages/magic.py
。它看起來像你使用它像:(代碼從here)
import magic
ms = magic.open(magic.MAGIC_NONE)
ms.load()
type = ms.file("/path/to/some/file")
print type
f = file("/path/to/some/file", "r")
buffer = f.read(4096)
f.close()
type = ms.buffer(buffer)
print type
ms.close()
至於你原來的問題: 「閱讀源代碼,盧克。」
的Django /核心/文件/ images.py:
"""
Utility functions for handling images.
Requires PIL, as you might imagine.
"""
from django.core.files import File
class ImageFile(File):
"""
A mixin for use alongside django.core.files.base.File, which provides
additional features for dealing with images.
"""
def _get_width(self):
return self._get_image_dimensions()[0]
width = property(_get_width)
def _get_height(self):
return self._get_image_dimensions()[1]
height = property(_get_height)
def _get_image_dimensions(self):
if not hasattr(self, '_dimensions_cache'):
close = self.closed
self.open()
self._dimensions_cache = get_image_dimensions(self, close=close)
return self._dimensions_cache
def get_image_dimensions(file_or_path, close=False):
"""
Returns the (width, height) of an image, given an open file or a path. Set
'close' to True to close the file at the end if it is initially in an open
state.
"""
# Try to import PIL in either of the two ways it can end up installed.
try:
from PIL import ImageFile as PILImageFile
except ImportError:
import ImageFile as PILImageFile
p = PILImageFile.Parser()
if hasattr(file_or_path, 'read'):
file = file_or_path
file_pos = file.tell()
file.seek(0)
else:
file = open(file_or_path, 'rb')
close = True
try:
while 1:
data = file.read(1024)
if not data:
break
p.feed(data)
if p.image:
return p.image.size
return None
finally:
if close:
file.close()
else:
file.seek(file_pos)
所以看起來它只是讀取文件1024個字節在同一時間,直到PIL說,這是一個圖像,然後停止。這顯然不能完整地檢查整個文件,所以它實際上取決於「覆蓋我的圖像上傳安全性」的含義:非法數據可以附加到圖像並通過您的網站傳遞。有人可以通過上傳很多垃圾或一個非常大的文件來阻止您的網站。如果您沒有檢查任何上傳的標題或對圖像上傳的文件名做出假設,則可能容易受到注入攻擊。等等。
Django的驗證使用PIL經由形式上傳的圖像。 參見https://code.djangoproject.com/browser/django/trunk/django/forms/fields.py#L519
try:
# load() is the only method that can spot a truncated JPEG,
# but it cannot be called sanely after verify()
trial_image = Image.open(file)
trial_image.load()
# Since we're about to use the file again we have to reset the
# file object if possible.
if hasattr(file, 'reset'):
file.reset()
# verify() is the only method that can spot a corrupt PNG,
# but it must be called immediately after the constructor
trial_image = Image.open(file)
trial_image.verify()
...
except Exception: # Python Imaging Library doesn't recognize it as an image
raise ValidationError(self.error_messages['invalid_image'])
PIL文檔指出以下有關驗證():
嘗試,以確定是否該文件被破壞,而無需實際解碼 的圖像數據。如果此方法發現任何問題,則會引發合適的例外情況。此方法僅適用於新打開的圖像;如果 圖像已被加載,則結果未定義。另外,如果您在使用此方法後需要加載圖像 ,則必須重新打開 圖像文件。
你也應該注意到的ImageField使用形式上傳時纔會驗證。如果您自己保存模型(例如使用某種下載腳本),則不會執行驗證。
嗨邁克,我能看到這會派上用場,但它是多餘的,當用的ImageField結合起來呢?很明顯,ImageField執行某種類型的文件類型驗證 – Ben 2011-04-24 13:38:12
感謝您的更新,並且您對這些限制是正確的。我有一些策略來處理防止大文件上傳的問題。注射攻擊的可能性是我最擔心的問題。 – Ben 2011-04-24 17:33:57