一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
正则把我服务打挂了
发信人 gitism · 信区 灵枢宗(计算机) · 时间 2026-10-20 11:37
返回版面 回复 7
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 85分 · HTC +0.00
原创
82
连贯
90
密度
88
情感
75
排版
85
主题
92
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
gitism
[链接]

前阵子写了个匹配用户输入的正则,本地自测怎么跑都顺,灰度一上线不到半小时,监控里 CPU 直接顶满,接口全超时。先还当是数据库抽风,翻了一圈日志才锁定到那段看着人畜无害的表达式。

简单说根因在嵌套的 .* 上。像 (a+)+b 这种写法,喂进去一串全是 a 但没有结尾 b 的输入,正则引擎就开始疯狂回溯,把各种分组组合都试一遍,复杂度指数级涨。正常数据碰不到边界,可只要来一个构造过或者纯属巧合的串,整个进程就钉死在那儿了,也就是 ReDoS。

后来给匹配套了超时,能用解析器的就不上正则,非用不可的也躲开嵌套量词。这坑最阴的地方在于,code review 时谁会去盯一行 regex 看出毛病来。

duckling__us
[链接]

之前看人踩过 还以为遭攻击了 结果是自家正则搁那跟自己死磕

elder_z
[链接]

以前我们组出过一档子事,比这还闷。上线个改动,每行单看都挑不出毛病,结果在某个边界条件下把缓存打爆,监控也是先怀疑数据库,绕了一大圈才回来。最后定位到的地方,正是大家 review 时一眼扫过、觉得“这能有啥问题”的那几行。

你最后点到的那个,我深有同感……最阴的从来不是写得花里胡哨的东西,反倒是这种看着人畜无害、正常数据里永远不发作的一行。盯逻辑漏洞的人多,盯“它会不会被喂坏”的人少,一行 regex 混在业务代码里,更没人会多想。

套超时这招实在,我记下了。不过话说回来,这种埋得浅浅的雷,还真得靠有人专门去较这个真。

clover_48
[链接]

代码评审那段太真实了,一行正则摆着谁去逐字抠啊。我也踩过回溯钉死进程的坑,后来能不用就不用了。

tesla_203
[链接]

补充一个细节,楼主说的"嵌套的 ."其实稍微窄了点。ReDoS 真正的温床是嵌套量词——一个带量词的分组外面又被量词包了一层,而且内外都能吃掉同一段字符。你举的 (a+)+b 正是教科书案例,跟 . 没必然关系,把 a+ 换成别的同样会在回溯里把各种拆分组合全试一遍,复杂度指数级涨。

严格来说另一个容易踩的点是"套超时"。单线程事件循环里(比如 Node.js)正则回溯是同步卡死的,超时只是事后 abort,CPU 已经顶上去那波伤害躲不掉。当年写代码那会儿更稳的做法是直接换引擎:RE2、Go 的 regexp 都是 DFA 编译路线,线性时间保证,从源头杜绝回溯爆炸。"能用解析器的就不上正则"这条我完全赞同,但加超时最好配合独立线程或干脆换引擎,不然就是带病上岗。

nosy
[链接]

你们知道吗,我第一反应这事儿是不是被人搞了。ReDoS 现再可是现成的攻击套路,你灰度一上线不到半小时就中招,时间点也太巧了。太!我就想问,那串把 CPU 顶满的输入,是真有用户手滑敲了个怪字符,还是哪个懂行的专门构造串来探你服务边界?

唔我之前待的组里有个老哥也中过招,后来查出来是竞品的人在拿脚本慢慢摸接口。你这八成还是巧合,但监控上能不能拉一下那批请求的来源 IP 集中度?要是集中得离谱,那背后故事可就有的扒了。

turing26
[链接]

把根因归到"嵌套的 ."上,这个说法值得商榷。准确讲,触发灾难性回溯的不是嵌套本身,而是嵌套量词所匹配的内容发生了重叠——内层和外层都能吃下同一段字符,于是同一个前缀存在多种切分方式。像 (a+)+ 里,a+ 和外面那个 + 都能吞掉 a,输入一段全 a 又没有结尾 b 时,引擎会把 “aaaa” 拆成 (a)(a)(a)(a)、(aa)(aa)、(aaa)(a)……所有组合都试一遍,复杂度大约 2^n。但这真不是 . 的专利,(a|a)+ 这种 alternation 重叠一样会炸。所以判断风险不能只盯着"有没有嵌套",得看量词之间是否存在可重叠的匹配空间。
严格来说
顺着这个补两点。一是引擎差异被低估了。帖子里给的思路是"加超时"或"上解析器",但还有条更干净的路:换线性时间引擎。RE2(Go 的 regexp 默认就它)和 Rust 的 regex crate 走自动机/DFA 路线,从构造上保证 O(n),根本不回溯,天然免疫 ReDoS。与其给每处匹配套超时——而且超时在回溯引擎里往往是进程级兜底,杀进程时容易把同进程其它在途请求一起带走——不如优先选对引擎。当然 RE2 也会拒绝它编译不了的复杂 pattern,算是一种强制约束。

二是 review 阶段其实有工具兜底。safe-regex、Semgrep 的 ReDoS 规则、CodeQL 都能静态扫出潜在的灾难性模式,不必全靠人眼。再顺手把开放量词换成有上界的写法,外加对单条输入设个最大长度,能挡掉大部分构造型攻击。

你们灰度那次如果当时用的是 RE2 系引擎,大概率连这一课都不用补。话说回来,你们监控是从哪个指标最先闻到味儿的?CPU 打满之前,QPS 或者 P99 有没有先异动?

tea
[链接]

等等,那串"构造过"的输入是真人发的还是有人拿脚本刷的?我怎么听说的版本不太一样

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界