将用户输入用于 Python 的正则表达式是不是安全?
Posted
技术标签:
【中文标题】将用户输入用于 Python 的正则表达式是不是安全?【英文标题】:Is it safe to use user input for Python's regular expressions?将用户输入用于 Python 的正则表达式是否安全? 【发布时间】:2011-01-01 04:05:38 【问题描述】:我想让我的用户对某些功能使用正则表达式。我很好奇将用户输入传递给 re.compile() 的含义。我假设用户没有办法给我一个可以让他们执行任意代码的字符串。我想到的危险是:
-
用户可以传递引发异常的输入。
用户可能会传递导致正则表达式引擎花费很长时间或使用大量内存的输入。
1. 的解决方案很简单:捕获异常。我不确定 2 是否有一个好的解决方案。也许只是限制正则表达式的长度就可以了。
我还有什么需要担心的吗?
【问题讨论】:
【参考方案1】:我开发了一个程序,允许用户输入他们自己的正则表达式,你是对的 - 他们可以(并且确实)输入可能需要很长时间才能完成的正则表达式 - 有时比宇宙的生命周期还要长。更糟糕的是,在处理正则表达式时,Python 持有 GIL,因此它不仅会挂起正在运行正则表达式的线程,还会挂起整个程序。
限制正则表达式的长度不起作用,因为问题在于回溯。例如,在长度为 N 且不包含“x”的字符串上匹配正则表达式 r"(\S+)+x"
将回溯 2**N 次。在我的系统上,这需要大约一秒钟来匹配"a"*21
,并且每个附加字符的时间加倍,因此 100 个字符的字符串大约需要 19167393131891000 年才能完成(这是一个估计值,我没有计时)。
有关更多信息,请阅读 O'Reilly 的“掌握正则表达式”一书 - 其中有几章是关于性能的。
编辑 为了解决这个问题,我们编写了一个正则表达式分析函数,试图捕捉和拒绝一些更明显的退化情况,但不可能得到所有这些情况。
我们看到的另一件事是修补 re 模块以在它回溯太多次时引发异常。这是可能的,但需要更改 Python C 源代码并重新编译,因此不可移植。我们还提交了一个补丁来在匹配 python 字符串时释放 GIL,但我认为它没有被核心接受(python 只保存 GIL,因为正则表达式可以针对可变缓冲区运行)。
【讨论】:
我想我可以生成另一个进程并在它超时后杀死它? 生成和杀戮会起作用,但会为运行每场比赛增加相当大的开销。这是否是可接受的价格取决于您。 使用信号怎么样?停止一个很长的正则表达式会起作用吗? docs.python.org/library/signal.html【参考方案2】:为临时用户提供子集语言要简单得多。例如,fnmatch 中的 shell 通配规则。 SQL LIKE 条件规则是另一个例子。
将用户的语言翻译成适当的正则表达式,以便在运行时执行。
【讨论】:
【参考方案3】:编译正则表达式应该是相当安全的。虽然它编译成的不是严格意义上的 NFA(反向引用意味着它不是很干净),但它仍然应该很容易编译。
现在关于性能特征,这完全是另一个问题。由于回溯,即使是很小的正则表达式也可能具有指数时间特征。定义特定的特征子集并仅支持您自己翻译的非常有限的表达式可能会更好。
如果您真的想支持通用正则表达式,您要么必须信任您的用户(有时是一种选择),要么限制使用的空间和时间。我相信使用的空间仅由正则表达式的长度决定。
edit:正如 Dave 所指出的,显然全局解释器锁在正则表达式匹配期间被持有,这会使设置超时更加困难。如果是这种情况,您设置超时的唯一选择是在单独的进程中运行匹配。虽然不完全理想,但它是可行的。我完全忘记了multiprocessing
。兴趣点是this section 共享对象。如果您真的需要硬约束,那么可以选择单独的流程。
【讨论】:
使用单独的线程来实现超时不起作用,因为 python 在进行匹配时持有 GIL - 请参阅我的答案。即使你修补了 re 以释放 GIL,你也需要添加一些方法来杀死运行正则表达式的线程 - 这不是微不足道的! 我的错误,那真是太烦人了。我将编辑我的答案,稍微模糊但可能。【参考方案4】:没有必要使用 compile() 除非您需要重用许多不同的正则表达式。该模块已经缓存了最后的表达式。
如果您允许用户输入任何正则表达式,第 2 点(执行时)可能会非常困难。你可以用几个字符制作一个复杂的正则表达式,比如著名的(x+x+)+y
。我认为这是一个尚未普遍解决的问题。
一种解决方法可能是启动一个不同的线程并对其进行监视,如果它超过了允许的时间,则终止该线程并返回一个错误。
【讨论】:
【参考方案5】:我真的不认为仅仅通过将代码传递给 re.compile 来执行代码是不可能的。我理解它的方式,re.compile(或任何语言的任何正则表达式系统)将正则表达式字符串转换为finite automaton(DFA 或 NFA),尽管名称不祥,但它与执行无关任何代码。
【讨论】:
【参考方案6】:从技术上讲,您不需要使用re.compile()
对字符串执行正则表达式操作。事实上,如果您只执行一次操作,编译方法通常会更慢,因为初始编译会产生开销。
如果您担心“编译”这个词,那么就不要一起使用它,只需将原始表达式传递给 match
、search
等。无论如何,您最终可能会稍微提高代码的性能。
【讨论】:
我认为这不是重点。要执行实际搜索,match
无论如何都必须执行编译步骤,这是 OP 所担心的。以上是关于将用户输入用于 Python 的正则表达式是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章
Python - 用于将文本拆分为句子的正则表达式(句子标记)[重复]
用于防止输入任何不匹配正则表达式的输入的 jquery 插件