cgi.FieldStorage公司如果“filename=”未指定,with multipart/formdata尝试将二进制文件解码为UTF8

2024-05-16 00:12:11 发布

您现在位置:Python中文网/ 问答频道 /正文

当我使用cgi.FieldStorage来解析一个multipart/form-data请求(或者任何像Pyramid这样使用cgi.FieldStorage的web框架)时,我在处理从某些客户端上传的文件时遇到了问题,这些客户机没有在部件的Content-Disposition头中提供filename=file.ext。在

如果缺少filename=选项,FieldStorage()将尝试将文件内容解码为UTF-8并返回一个字符串。显然,许多文件是二进制的,而不是UTF-8,因此会产生错误的结果。在

例如:

>>> import cgi
>>> import io
>>> body = (b'--KQNTvuH-itP09uVKjjZiegh7\r\n' +
...         b'Content-Disposition: form-data; name=payload\r\n\r\n' +
...         b'\xff\xd8\xff\xe0\x00\x10JFIF')
>>> env = {
...     'REQUEST_METHOD': 'POST',
...     'CONTENT_TYPE': 'multipart/form-data; boundary=KQNTvuH-itP09uVKjjZiegh7',
...     'CONTENT_LENGTH': len(body),
... }
>>> fs = cgi.FieldStorage(fp=io.BytesIO(body), environ=env)
>>> (fs['payload'].filename, fs['payload'].file.read())
(None, '����\x00\x10JFIF')

浏览器和大多数HTTP库确实包含用于文件上载的filename=选项,但我目前处理的客户机没有这样做(根据规范,省略filename似乎是有效的)。在

目前,我正在使用一种相当老套的解决方法,将FieldStorage子类化,并将相关的Content-Disposition头替换为具有以下文件名的头:

^{pr2}$

在我的第一个测试中使用bodyenv,现在可以这样做了:

>>> class TestFieldStorage(FileFieldStorage):
...     _file_fields = ('payload',)
>>> fs = TestFieldStorage(fp=io.BytesIO(body), environ=env)
>>> (fs['payload'].filename, fs['payload'].file.read())
('file_name', b'\xff\xd8\xff\xe0\x00\x10JFIF')

有没有什么方法可以避免这种黑客攻击并告诉FieldStorage不要解码为UTF-8?如果您可以提供encoding=None或其他东西,那就太好了,但它似乎不支持这一点。在


Tags: 文件formenvdatabodycontentfilenamefs
2条回答

最后我使用了一个更简单的FieldStorage子类来解决这个问题,所以我把它作为答案发布在这里。您不必重写__init__并将文件名添加到Content-Disposition头中,而只需将.filename属性重写为返回文件名的属性(如果没有为该输入提供文件名):

class MyFieldStorage(cgi.FieldStorage):
    @property
    def filename(self):
        if self._original_filename is not None:
            return self._original_filename
        elif self.name == 'payload':
            return 'file_name'
        else:
            return None

    @filename.setter
    def filename(self, value):
        self._original_filename = value

另外,正如@bobince的回答所指出的,您可以使用surrogateescape错误处理程序,然后将其编码回字节。这有点迂回,但也可能是最简单的解决方法:

^{pr2}$

I have trouble processing file uploads from certain clients which don't provide a filename=file.ext in the part's Content-Disposition header.

filename=参数实际上是服务器端确定部件代表文件上载的唯一方法。如果客户机省略了这个参数,它实际上并不是发送一个文件上传,而是一个纯文本表单字段。在这样一个字段中发送任意二进制数据在技术上仍然是合法的,但是包括Pythoncgi的许多服务器环境都会对此感到困惑。在

It would be nice if you could provide encoding=None or something

如果将errors设置为surrogateescape,则至少可以从解码字符中恢复原始字节。在

相关问题 更多 >