文章目錄

之前同事发来一个邮箱匹配的正则表达式,^([a-z0-9A-Z]+[-|\\.|_]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$,说匹配的时候会卡住, 测试一下re.match("^([a-z0-9A-Z]+[-|\\.|_]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$", "sdfwerwerwerwerwsdfsdfsdfsdfsdfsdf"), 果然卡死 。找了很久没发现原因,后来看到浅析ReDoS的原理与实践后,才恍然大悟。([a-z0-9A-Z]+[-|\\.|_]?)+就是(a+)+这种形式, 而(a+)+这种类型的正则匹配时回溯次数特别多,很容易引起正则攻击.

类似的正则有

  • (a+)+
  • ([a-zA-Z]+)*
  • (a|aa)+
  • (a|a?)+
  • (.*a){x} for x > 10

查看《正则指引》,书中给了一个Python版本的正则表达式。r'^(?!\.)(?![\w.]*?\.\.)[\w.]{1,64}@(?=[-a-zA-Z0-9.]{0,255}(?![-a-zA-Z0-9.]))((?!-)[-a-zA-Z0-9]{1,63}\.)*(?!-)[-a-zA-Z0-9]{1,63}$'

其中@之前的部分是用户名,其长度不超过64个字符,所以是[\w.]{1,64}, 用户名不能以点号开头,所以添加环视(?!.), 同时用户名不能包含连续点号, 所以需要添加环视(?![\w.]*?\.\.)

@后面的部分是主机名,主机名分为很多段,每段长度不超过63,所以是[-a-zA-Z0-9]{1,63}, 且每段不能以横线开头,这一点用环视(?!-)保证,总长度不能超过255,用(?=[-a-zA-Z0-9.]{0,255}(?![-a-zA-Z0-9.]))来保证。主机名可以看做是”段+点号”的重复,所以是((?!-)[-a-zA-Z0-9]{1,63}\.)*, 最后的段必须出现,所以再添加一个(?!-)[-a-zA-Z0-9]{1,63}

看到晚上很多错误的例子,https://blog.csdn.net/xxm282828/article/details/43549959https://blog.csdn.net/bug_love/article/details/78799277https://www.oschina.net/code/snippet_1021818_47946,抄来抄去,都是错的。

文章目錄