对Python';使用用户输入是否安全;什么是正则表达式?

对Python';使用用户输入是否安全;什么是正则表达式?,python,regex,user-input,sanitize,Python,Regex,User Input,Sanitize,我想让我的用户对一些功能使用正则表达式。我很好奇将用户输入传递给re.compile()有什么意义。我假设用户没有办法给我一个字符串,可以让他们执行任意代码。我想到的危险是: 用户可以传递引发异常的输入。 用户可以传递导致正则表达式引擎花费很长时间或占用大量内存的输入 1的解决方案。很容易:捕获异常。我不确定2是否有一个好的解决方案。也许仅仅限制正则表达式的长度就行了 还有什么我需要担心的吗?我真的不认为仅仅通过将代码传递到重新编译来执行代码是可能的。按照我的理解,re.compile(或任

我想让我的用户对一些功能使用正则表达式。我很好奇将用户输入传递给re.compile()有什么意义。我假设用户没有办法给我一个字符串,可以让他们执行任意代码。我想到的危险是:

  • 用户可以传递引发异常的输入。
    • 用户可以传递导致正则表达式引擎花费很长时间或占用大量内存的输入
  • 1的解决方案。很容易:捕获异常。我不确定2是否有一个好的解决方案。也许仅仅限制正则表达式的长度就行了


    还有什么我需要担心的吗?

    我真的不认为仅仅通过将代码传递到重新编译来执行代码是可能的。按照我的理解,re.compile(或任何语言中的任何regex系统)将regex字符串转换为(DFA或NFA),尽管有一个不祥的名称“compile”,但它与任何代码的执行无关

    从技术上讲,您不需要使用
    re.compile()
    对字符串执行正则表达式操作。事实上,如果只执行一次操作,编译方法通常会比较慢,因为初始编译会带来开销


    如果您担心“compile”这个词,那么就避免使用它,只需将原始表达式传递给
    match
    search
    ,等等。无论如何,您可能会稍微提高代码的性能。

    除了需要重用大量不同的正则表达式外,不必使用compile()。模块已缓存最后的表达式

    如果允许用户输入任何正则表达式,那么第2点(执行时)可能是非常困难的。您可以用很少的字符创建一个复杂的regexp,比如著名的
    (x+x+)+y
    一个。我认为这是一个尚未得到普遍解决的问题。
    解决方法可以是启动不同的线程并对其进行监视,如果超过允许的时间,则终止该线程并返回错误。

    编译正则表达式应该是相当安全的。虽然它编译成的东西并不是严格意义上的NFA(反向引用意味着它没有那么干净),但它仍然应该有点简单

    至于性能特征,这完全是另一个问题。由于回溯,即使是一个小的正则表达式也可能具有指数时间特性。最好定义一个特定的功能子集,只支持您自己翻译的非常有限的表达式

    如果您真的想支持通用正则表达式,您必须信任您的用户(有时是一个选项),或者限制使用的空间和时间。我相信使用的空间只由正则表达式的长度决定


    编辑:正如Dave所指出的,显然全局解释器锁在正则表达式匹配期间被保持,这将使设置超时变得更加困难。如果是这种情况,设置超时的唯一选项是在单独的进程中运行匹配。虽然并不完全理想,但它是可行的。我完全忘了。兴趣点在于共享对象。如果你真的需要硬约束,那么单独的过程就是最好的选择。

    我开发了一个程序,允许用户输入他们自己的正则表达式,你是对的——他们可以(而且确实)输入正则表达式,这可能需要很长时间才能完成,有时甚至比整个宇宙的生命周期还要长。更糟糕的是,在处理正则表达式时,Python持有GIL,因此它不仅会挂起运行正则表达式的线程,还会挂起整个程序

    限制正则表达式的长度是行不通的,因为问题是回溯。例如,在长度为N且不包含“x”的字符串上匹配regex
    r”(\S++x“
    )将回溯2**N次。在我的系统上,与
    “a”*21
    匹配大约需要一秒钟,每增加一个字符,时间就会加倍,因此100个字符的字符串大约需要191673931891000年才能完成(这是一个估计,我没有计时)

    有关更多信息,请阅读O'Reilly的书《掌握正则表达式》——这本书有两章介绍性能

    编辑 为了解决这个问题,我们编写了一个正则表达式分析函数,试图捕获并拒绝一些更明显的退化情况,但不可能获得所有这些情况


    我们看到的另一件事是修补re模块,如果它回溯太多次,就会引发异常。这是可能的,但需要更改PythonC源代码并重新编译,因此不可移植。我们还提交了一个补丁,在与python字符串匹配时释放GIL,但我认为它没有被纳入核心(python只保留GIL,因为正则表达式可以在可变缓冲区上运行)。

    对于普通用户来说,为他们提供子集语言要简单得多。例如,shell的全局规则。类似SQL的条件规则是另一个例子


    将用户的语言转换为适当的正则表达式,以便在运行时执行。

    我认为这有点离题。要执行实际的搜索,
    match
    无论如何都必须执行编译步骤,这正是OP所担心的。使用单独的线程实现超时不起作用,因为python在执行匹配时持有GIL-请参阅我的答案。即使您修补了re以释放GIL,您也需要添加一些方法来杀死运行regex的线程——这不是小事!我的错误,那是相当令人讨厌的。我将编辑一些更模糊但可能的答案。我想我可以生成另一个进程,如果它在太长时间后超时,则将其杀死?生成和杀死将起作用,但会为运行每个比赛增加相当大的开销。这是否是一个可以接受的价格