术语表

什么是 TOCTOU:状态在检查与使用之间发生变化

TOCTOU(time-of-check to time-of-use)是一种竞争条件,即检查之后状态发生变化。介绍其工作原理及防御方法:将检查与使用合并为一步。

发布于 2026-11-15 更新于 2026-11-15 2 分钟阅读
本页目录

TOCTOU(time-of-check to time-of-use)是指某个事物的状态在被检查的时刻与被使用的时刻之间发生变化的问题。本页介绍其工作原理及预防方法(不包含攻击步骤)。

什么是 TOCTOU

程序常以“先检查,若无问题再使用”的方式工作,例如“检查此文件是否可写,然后打开并写入”。但检查与使用之间存在一段很短的时间间隔。如果另一个程序或请求在这段间隔中改变了目标,检查的结果就不再成立。

MITRE 的 CWE-367 将其描述为:产品在使用资源前检查其状态,而该状态可能在检查与使用之间发生变化,从而使检查失效。CWE(Common Weakness Enumeration,通用缺陷枚举)是漏洞背后缺陷的共享目录。TOCTOU 是竞争条件的一种,其结果取决于同时运行的各项操作的时序。

为什么会发生

1. 检查:此文件可以写入吗?→ 是
2. 间隔:另一个进程替换了该名称指向的对象
↓ 检查结果不再与实际情况相符
3. 使用:程序写入了一个它从未检查过的目标
当检查与使用是分开的步骤时,目标可能在两者之间被替换,检查结果随之失效。

根据 CWE-367,其后果可能包括:访问本应禁止访问的资源、资源被以非预期方式更改,以及日志缺失。这段间隔可能很短,但在同时运行大量任务的系统中,它会被反复触发,而不仅仅是偶然发生。

常见出现的场景

场景检查使用出现的问题
文件处理存在性或权限检查打开或写入检查之后文件被替换
Web 应用库存或余额SELECT 剩余数量UPDATE 减少数量两个并发请求都通过检查并超出限制
支付与注册是否仍未处理?执行操作重复提交导致执行两次
文件上传检查内容存储或发布检查之后内容被更改

关于上传检查,另请参阅 文件上传漏洞。

防御:不要将检查与使用分开

1

不要在使用前进行检查

CWE-367 最基本的建议是不要在使用之前进行检查。直接使用资源,并在失败时处理错误。这样就消除了间隔本身。

2

使用文件描述符而非路径

先打开文件,然后通过已打开的文件描述符检查状态并写入。反复查找同一路径名可能每次指向不同的目标。O_NOFOLLOW(不跟随符号链接)等标志,以及 openat(相对于目录打开)等调用也有帮助;可用性取决于操作系统。

3

数据库使用条件更新和事务

不要采用“先 SELECT 检查,再 UPDATE”,而是使用包含条件的单条 UPDATE(例如,仅当数量为 1 或以上时才减少)。当涉及多条语句时,使用具有合适隔离级别的事务和行锁。

4

在检查之前加锁

如果使用锁,应如 CWE-367 所述,在检查之前获取锁。在检查之后加锁无法保证你检查的目标就是你使用的目标。

5

使用幂等键防止重复执行

对于支付和注册,为每个请求分配唯一的键(幂等键),并确保每个键只执行一次操作。

如何检查自己的代码

请在你自己开发或管理的代码中进行。

  1. 查找检查代码与使用代码分属不同调用的位置(先检查存在性再打开;先 SELECT 再 UPDATE)
  2. 对每处位置,判断能否去掉检查,并将该步骤合并为单个操作
  3. 编写测试,同时发送同一请求多次,确认只有一次被执行
  4. 切勿对他人的服务进行此类测试;请使用你自己的环境

本站观点:将检查置于与使用相同的操作之中

TOCTOU 并非“有人忘记了检查”。检查确实存在,只是与使用分离了。添加更多检查代码无法解决问题。正确的修复方法是将检查置于与使用相同的原子操作之中,而不是将其作为额外步骤添加。在学习 IDOR 的同时理解它,有助于代码审查:两者都是“检查存在但未发挥作用”的缺陷。

主要来源

  • MITRE CWE-367:Time-of-check Time-of-use (TOCTOU) Race Condition — cwe.mitre.org(定义、后果、缓解措施)
  • 具体技术(O_NOFOLLOW、openat、幂等键、事务)作为通用实现实践进行描述;可用性因操作系统和数据库而异,请查阅各产品的官方文档

延伸阅读

分类编号

CWE 对导致漏洞的弱点类型进行分类,MITRE ATT&CK 对攻击者的技术进行分类。两者均由 MITRE 维护,广泛用于标注漏洞信息和检测规则。

CWE™: Copyright © 2006–2026, The MITRE Corporation. CWE is a trademark of The MITRE Corporation. The MITRE Corporation hereby grants you a non-exclusive, royalty-free license to use CWE for research, development, and commercial purposes. Any copy you make for such purposes is authorized on the condition that you reproduce MITRE’s copyright designation and this license in any such copy. CWE Terms of Use

FAQ

QTOCTOU 是什么意思?
A

它是 time-of-check to time-of-use 的缩写,指资源的状态在被检查的时刻与被使用的时刻之间发生了变化。MITRE 的 CWE-367 将其描述为:产品在使用资源前检查其状态,而该状态可能在检查与使用之间发生变化,从而使检查失效。它属于竞争条件的一种。

QTOCTOU 常出现在哪里?
A

典型场景是文件处理:程序先检查文件是否存在或是否可写,再通过单独的调用打开文件,而文件可能在两者之间被替换。在 Web 应用和数据库中也会出现,例如“检查库存,然后确认订单”或“检查余额,然后扣款”,当检查与更新是分开的步骤时就可能发生。

Q主要的防御方法是什么?
A

将检查与使用合并为一个原子操作。CWE-367 指出,最基本的建议是不要在使用之前进行检查。对于文件,应先打开文件,再通过已打开的文件描述符进行检查或操作。对于数据库,可使用条件更新,或带锁的事务。

Q添加锁一定就安全吗?
A

锁必须在检查之前获取,而不是之后。CWE-367 指出,应确保加锁发生在检查之前,而不是之后。在检查之后加锁并不能保证你检查的对象就是你使用的对象。如果锁的范围过窄,或其他程序忽略了该锁,它也起不到作用。

QTOCTOU 会影响 Web 应用吗?
A

会。例如,在检查上传文件之后再保存;读取优惠券或库存数量后再扣减;以及不能被重复执行的支付处理程序。如果同一请求同时到达两次,两次都可能通过检查。应将更新做成一个条件操作,并使用幂等键来防止重复执行。

Q如何在自己的代码中查找 TOCTOU?
A

查找检查代码与使用代码分属不同行或不同调用的位置。在文件代码中,表现为先做存在性或权限检查,再按路径打开。在数据库代码中,表现为先用 SELECT 检查,再执行 UPDATE。然后判断能否去掉检查,将其合并为一个操作,并添加并发请求测试。