Ben*_*Ben 5 security django file-upload django-models
我有很多用户上传的内容,我想验证上传的图像文件实际上不是恶意脚本.在Django文档中,它声明了ImageField:
"从FileField继承所有属性和方法,但也验证上传的对象是有效的图像."
这完全准确吗?我已经读过压缩或以其他方式操作图像文件是一个很好的验证测试.我假设PIL做了这样的事......
ImageField是否会大大覆盖我的图片上传安全性?
小智 7
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'])
Run Code Online (Sandbox Code Playgroud)
PIL文档说明了以下关于verify()的内容:
尝试确定文件是否已损坏,而不实际解码图像数据.如果此方法发现任何问题,则会引发适当的异常.此方法仅适用于新打开的图像; 如果图像已加载,则结果未定义.此外,如果需要在使用此方法后加载图像,则必须重新打开图像文件.
您还应注意,ImageField仅在使用表单上载时进行验证.如果您自己保存模型(例如使用某种下载脚本),则不会执行验证.
另一个测试是使用file命令。它检查文件中是否存在“幻数”以确定其类型。在我的系统上,该file软件包包括libmagic一个基于 ctypes 的包装器/usr/lib64/python2.7/site-packages/magic.py。看起来你像这样使用它:
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()
Run Code Online (Sandbox Code Playgroud)
(代码来自这里。)
至于你最初的问题:“阅读来源,卢克。”
django/core/files/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)
Run Code Online (Sandbox Code Playgroud)
所以看起来它一次只读取文件 1024 个字节,直到 PIL 说这是一个图像,然后停止。这显然不会对整个文件进行完整性检查,因此这实际上取决于您所说的“覆盖我的图像上传安全性”的含义:非法数据可能会附加到图像并通过您的网站传递。有人可以通过上传大量垃圾或非常大的文件来对您的网站进行 DOS。如果您不检查任何上传的标题或对图像的上传文件名做出假设,您可能容易受到注入攻击。等等。
| 归档时间: |
|
| 查看次数: |
4960 次 |
| 最近记录: |