TOCTOU(time-of-check to time-of-use)是指某個事物的狀態在被檢查的時刻與被使用的時刻之間發生改變的問題。本頁說明其運作方式與預防方法(不含攻擊步驟)。
什麼是 TOCTOU
程式常以「先檢查,若看起來沒問題再使用」的方式運作,例如「檢查此檔案可否寫入,再開啟並寫入」。但檢查與使用之間存在一段很短的時間差。若在這段時間內,其他程式或請求改變了目標,檢查結果便不再成立。
MITRE 的 CWE-367 將其描述為:產品在使用資源前檢查其狀態,但狀態可能在檢查與使用之間改變,使檢查結果失效。CWE(Common Weakness Enumeration,通用弱點列舉)是漏洞背後弱點的共通目錄。TOCTOU 是競爭條件的一種,其結果取決於同時執行之事件的時序。
為何會發生
根據 CWE-367,其後果可能包括:存取本應禁止存取的資源、資源被以非預期方式改變,以及缺少日誌紀錄。空隙可能很短,但在同時執行大量工作的系統中,它會被反覆觸及,而非僅是偶然發生。
常見出現的位置
| 位置 | 檢查 | 使用 | 出錯的情況 |
|---|---|---|---|
| 檔案處理 | 存在或權限檢查 | 開啟或寫入 | 檢查後檔案被替換 |
| 網頁應用程式的庫存或餘額 | SELECT 剩餘數量 | UPDATE 扣減數量 | 兩個並行請求都通過檢查而超出上限 |
| 付款與註冊 | 是否仍未處理? | 執行動作 | 重複送出導致執行兩次 |
| 檔案上傳 | 檢查內容 | 儲存或發佈 | 檢查後內容被改變 |
關於上傳檢查,另請參閱 檔案上傳漏洞。
防禦:不要將檢查與使用分開
不要在使用前進行檢查
CWE-367 最基本的建議是不要在使用之前進行檢查。直接使用資源,若失敗則處理錯誤。這樣便能消除空隙本身。
使用檔案描述子,而非路徑
先開啟檔案,然後檢查狀態並透過已開啟的檔案描述子寫入。重複查詢同一路徑名稱,每次都可能指向不同的目標。O_NOFOLLOW(不跟隨符號連結)等旗標,以及 openat(相對於目錄開啟)等呼叫也有幫助;可用的選項取決於作業系統。
資料庫使用條件式更新與交易
不要採用「SELECT 檢查,再 UPDATE」,而是使用包含條件的單一 UPDATE(例如僅在數量為 1 或以上時才遞減)。若涉及多個陳述式,請使用具適當隔離層級與資料列鎖定的交易。
在檢查之前鎖定
若使用鎖定,請如 CWE-367 所述,在檢查之前取得鎖。在檢查之後才鎖定,無法保證你檢查的目標就是你使用的目標。
以冪等性金鑰防止重複執行
對於付款與註冊,為每個請求指定唯一金鑰(冪等性金鑰),並確保每個金鑰只執行一次該動作。
如何檢查自己的程式碼
請在你自行開發或管理的程式碼中進行。
- 找出檢查程式碼與使用程式碼是獨立呼叫的地方(先檢查存在再開啟;先 SELECT 再 UPDATE)
- 對每一處,評估能否取消檢查,並將該步驟合併為單一操作
- 撰寫測試,同時對同一請求送出數次,確認只有一次生效
- 切勿對他人的服務進行此類測試;請使用你自己的環境
本站觀點:將檢查置於與使用相同的操作之中
TOCTOU 並非「有人忘了檢查」。檢查其實存在,只是與使用分開了。增加更多檢查程式碼無法解決問題。正確的修正是將檢查置於與使用相同的原子操作之中,而不是將其當作額外步驟加入。在審查程式碼時,若同時學習 IDOR 會有幫助:兩者都是「檢查存在,卻未發揮作用」的缺陷。
主要來源
- MITRE CWE-367:Time-of-check Time-of-use (TOCTOU) Race Condition — cwe.mitre.org(定義、後果、緩解措施)
- 具體技術(O_NOFOLLOW、openat、冪等性金鑰、交易)係以一般實作做法說明;可用性因作業系統與資料庫而異,請查閱各產品的官方文件
延伸閱讀
- 詞彙表:什麼是路徑遍歷 · 什麼是 IDOR · 什麼是 OWASP Top 10
- 指南:檔案上傳漏洞
- 詞彙表:什麼是 CVE