使用用户输入作为Python正则表达式安全吗?

15 投票
6 回答
3482 浏览
提问于 2025-04-15 17:36

我想让我的用户在某些功能中使用正则表达式。我很好奇把用户输入传给re.compile()会有什么影响。我假设用户不可能给我一个字符串,让他们执行任意代码。我想到的危险有:

  1. 用户可能会输入导致异常的内容。
    • 用户可能会输入让正则表达式引擎运行很久,或者消耗大量内存的内容。

对于第一个问题,解决办法很简单:捕获异常。我不太确定第二个问题有没有好的解决办法。也许限制正则表达式的长度就可以了。

还有其他我需要担心的事情吗?

6 个回答

4

编译正则表达式应该是比较安全的。虽然它编译出来的东西不完全是NFA(因为有回溯引用,这让事情变得有点复杂),但总体来说,编译过程应该还是比较简单的。

至于性能方面,这又是另一个问题。即使是一个小的正则表达式,也可能因为回溯而导致运行时间呈指数级增长。可能更好的做法是定义一些特定的功能,只支持非常有限的表达式,这样你可以自己进行转换。

如果你真的想支持通用的正则表达式,你要么得信任你的用户(有时候这是个选项),要么就得限制使用的空间和时间。我相信,使用的空间只和正则表达式的长度有关。

补充一下:正如Dave所提到的,似乎在进行正则匹配时会持有全局解释器锁,这会让设置超时变得更加困难。如果真是这样,设置超时的唯一办法就是在一个单独的进程中运行匹配。虽然这并不是最理想的方案,但也是可行的。我完全忘了提到multiprocessing。值得关注的是这一部分,讲的是进程间共享对象。如果你真的需要严格的限制,使用单独的进程是个不错的选择。

6

对于普通用户来说,提供一个简化的语言会简单很多。比如,Shell中的通配符规则可以在fnmatch中找到。还有SQL中的LIKE条件规则也是一个例子。

把用户输入的语言转换成合适的正则表达式,以便在运行时执行。

22

我曾经做过一个程序,允许用户输入自己的正则表达式。你说得对,他们确实会输入一些需要很长时间才能完成的正则表达式,有时候甚至比宇宙的寿命还要长。更糟糕的是,在处理正则表达式时,Python会占用全局解释器锁(GIL),这不仅会让运行正则表达式的线程卡住,还会导致整个程序都停下来。

限制正则表达式的长度是没用的,因为问题出在回溯上。举个例子,匹配正则表达式 r"(\S+)+x" 在一个长度为N且不包含"x"的字符串上,会进行2的N次方次的回溯。在我的系统上,匹配 "a"*21 大约需要一秒钟,而每增加一个字符,时间就会翻倍,所以一个100个字符的字符串大约需要19167393131891000年才能完成(这是个估算,我没有实际计时)。

想了解更多信息,可以看看O'Reilly出版的《掌握正则表达式》这本书,里面有几章讲性能的内容。

编辑
为了解决这个问题,我们写了一个正则表达式分析函数,试图捕捉并拒绝一些明显的问题情况,但不可能把所有情况都处理掉。

我们还考虑过修改re模块,让它在回溯次数过多时抛出异常。这是可行的,但需要修改Python的C源代码并重新编译,所以不太方便移植。我们还提交了一个补丁,希望在匹配Python字符串时释放GIL,但我觉得这个补丁没有被核心代码接受(Python之所以会占用GIL,是因为正则表达式可以在可变缓冲区上运行)。

撰写回答