当我使用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
头替换为具有以下文件名的头:
在我的第一个测试中使用body
和env
,现在可以这样做了:
>>> 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
或其他东西,那就太好了,但它似乎不支持这一点。在
最后我使用了一个更简单的
FieldStorage
子类来解决这个问题,所以我把它作为答案发布在这里。您不必重写__init__
并将文件名添加到Content-Disposition
头中,而只需将.filename
属性重写为返回文件名的属性(如果没有为该输入提供文件名):另外,正如@bobince的回答所指出的,您可以使用
^{pr2}$surrogateescape
错误处理程序,然后将其编码回字节。这有点迂回,但也可能是最简单的解决方法:filename=参数实际上是服务器端确定部件代表文件上载的唯一方法。如果客户机省略了这个参数,它实际上并不是发送一个文件上传,而是一个纯文本表单字段。在这样一个字段中发送任意二进制数据在技术上仍然是合法的,但是包括Python
cgi
的许多服务器环境都会对此感到困惑。在如果将
errors
设置为surrogateescape
,则至少可以从解码字符中恢复原始字节。在相关问题 更多 >
编程相关推荐