Python的字符串.format()能否对不可信的格式字符串变得安全?

27 投票
2 回答
13582 浏览
提问于 2025-04-17 18:46

我正在开发一个网页应用,用户可以输入字符串,然后服务器会把一些变量替换进去。

我希望能使用 PEP 3101format() 语法,并且我在考虑是否可以重写 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格式字符串规范,并用正则表达式禁止用户不想要的功能。

撰写回答