TOCTOU (time-of-check to time-of-use) — это проблема, при которой состояние объекта меняется между моментом его проверки и моментом использования. На этой странице объясняется, как это работает и как это предотвратить (без шагов атаки).
Что такое TOCTOU
Программы часто работают по принципу «проверь, и если всё в порядке, используй»: например, «проверить, что в файл можно писать, затем открыть и записать». Но между проверкой и использованием есть небольшой промежуток времени. Если в этом промежутке другая программа или запрос меняет цель, результат проверки перестаёт быть верным.
В CWE-367 от MITRE это описано как продукт, который проверяет состояние ресурса перед использованием, причём состояние может измениться между проверкой и использованием так, что проверка теряет силу. 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, ключи идемпотентности, транзакции) описаны как общая практика реализации; доступность зависит от ОС и базы данных, поэтому проверяйте официальную документацию каждого продукта
Читайте дальше
- Глоссарий: Что такое path traversal · Что такое IDOR · Что такое OWASP Top 10
- Руководство: Уязвимости при загрузке файлов
- Глоссарий: Что такое CVE