一句话定义
TOCTOU(Time-of-Check to Time-of-Use,检查与使用的时间差)是一类漏洞模式:程序先确认某个条件成立,再根据这个确认去执行操作;而在“确认”和“操作”这两个时刻之间,条件被换掉了,那次检查就等于白做。
问题出在“两个时刻”
生活里的版本是这样的:中介带你看了一套房,采光、楼层、邻居都满意(check),你签了合同,一周后搬进去(use),发现住的是另一间。看房时看到的一切都是真的,只是你看的和最终拿到的不是同一个东西。
程序里最经典的场景是文件。程序先看看某个路径存不存在、能不能写,觉得没问题,然后拿着这个路径去打开文件。这两个动作之间隔着若干毫秒——足够攻击者把那个路径换成一个指向系统敏感文件的软链接(symbolic link)。检查时它是个普通临时文件,使用时它变成了别的东西,这就是历史上的软链接攻击。凡是“先按名字检查、再按名字使用”的写法,都存在这个窗口期。
和“竞态条件”的关系
| 概念 | 关注点 |
|---|---|
| 竞态条件(Race Condition) | 更宽泛:多个执行流按不同顺序交错,导致结果不确定、难以复现 |
| TOCTOU | 竞态的一个特例:窗口期恰好卡在“检查”和“使用”两步之间,且常被攻击者主动利用 |
| 原子操作 | 它属于解法,用于把检查和使用合成不可分割的一步 |
换句话说,TOCTOU 是竞态条件在安全语境下的具体表现。库存超卖、并发下单把余额扣成负数,本质上是同一件事,只是没人“攻击”,是用户自己撞上了。
对从业者的意义
核心原则只有一条:不要把检查和操作拆成两步,让“用的时候顺便确认”成为唯一路径。
- 文件操作:一旦打开拿到句柄(file descriptor),后续校验和读写都基于句柄做,而不是反复引用那个可能已经变了的路径名。
- 数据库:不要“先查库存再更新”,而用带条件的原子更新(如
UPDATE ... WHERE stock > 0)或唯一约束来保证条件,避免拆成两步。 - 权限:前端校验过不代表服务端可以省;服务端也不要“先查一次权限、再执行一堆操作”,校验要贴近操作本身。
对普通人的意义
凡是“你看到的”和“你最终拿到的”之间存在时间差的事,都值得多问一句:中间会不会被换掉?秒杀名额、二手交易的商品链接、审批通过的报销单、扫码解锁的那辆车。系统里所有的“确认无误”,都只对确认的那一瞬间负责。
