Python的字符串.format()能否对不可信的格式字符串变得安全?
我正在开发一个网页应用,用户可以输入字符串,然后服务器会把一些变量替换进去。
我希望能使用 PEP 3101 的 format()
语法,并且我在考虑是否可以重写 Formatter
中的方法,以确保处理不可信输入时的安全性。
目前我看到使用 .format()
的一些风险:
- 填充功能允许你指定任意长度,比如
'{:>9999999999}'.format(..)
可能会让服务器内存耗尽,造成拒绝服务攻击(DOS)。我需要禁用这个功能。 - 格式化可以访问对象内部的字段,这虽然有用,但也有点可怕,因为你可以访问一些特殊变量,还能深入到标准库的某些部分。谁也不知道哪里可能有
getattr()
,它可能会有副作用或者返回一些秘密信息。我会通过重写get_field()
来限制属性和索引的访问。 - 当然,我还需要处理一些异常。
我的假设是:
- 传统的 C 语言格式字符串漏洞不适用于 Python,因为在 Python 中指定参数是经过边界检查的集合访问,而不是直接从线程栈中弹出。
- 我使用的网页框架会对每个替换到页面模板中的变量进行转义,只要这是输出前的最后一步,我就能避免因为解转义而导致的跨站脚本攻击。
你怎么看?可能吗?不可能?只是不明智吗?
编辑:Armin Ronacher 提到,如果不过滤特殊变量的访问,可能会导致信息泄露,但他似乎认为保护 format()
是可行的:
{local_foo.__init__.__globals__[secret_global]}
小心使用 Python 的新式字符串格式 | Armin Ronacher 的想法和写作
就我个人而言,我在我的产品中并没有走不可信的 format()
这条路,但为了完整性我在更新这部分内容
2 个回答
4
这个简单的格式化器重写了功能,阻止用户访问某些属性。不过,它仍然可以进行格式化和类型转换。
from string import Formatter
class SafeFormatter(Formatter):
def get_field(self, field_name, args, kwargs):
if '.' in field_name or '[' in field_name:
raise Exception('Invalid format string.')
return super().get_field(field_name,args,kwargs)
form = SafeFormatter()
fname = form.format(format,num=1,id='hello')
9
很好的直觉。没错,攻击者如果能随意提供格式字符串,这在Python中就是一个安全隐患。
- 最简单的处理方式是防止服务拒绝攻击(DoS)。在这种情况下,限制字符串的大小或者字符串中操作符的数量可以减轻这个问题。应该有一个设置,让正常用户不需要生成变量数量超过X的字符串,这样计算量就不会被利用来进行DoS攻击。
- 能够访问对象中的属性可能会很危险。不过,我认为
Object
这个父类并没有什么有用的信息。提供给格式的对象必须包含一些敏感信息。无论如何,这种类型的表示法可以通过正则表达式来限制。 - 如果格式字符串是用户提供的,那么用户可能需要知道错误信息来进行调试。然而,错误信息可能包含敏感信息,比如本地路径或类名。一定要限制攻击者能获取的信息。
查看一下Python格式字符串规范,并用正则表达式禁止用户不想要的功能。