一个错误的邮箱匹配正则表达式
之前同事发来一个邮箱匹配的正则表达式,^([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/43549959, https://blog.csdn.net/bug_love/article/details/78799277, https://www.oschina.net/code/snippet_1021818_47946,抄来抄去,都是错的。