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와 함께 학습해 보세요. 둘 다 "확인은 존재하지만 제 역할을 하지 못하는" 버그입니다.
출처(1차 자료)
- MITRE CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition — cwe.mitre.org (정의, 결과, 완화 방법)
- 구체적인 기법(O_NOFOLLOW, openat, 멱등성 키, 트랜잭션)은 일반적인 구현 관행으로 설명되었으며, 사용 가능 여부는 운영체제와 데이터베이스에 따라 다르므로 각 제품의 공식 문서를 확인하세요
다음으로 읽을거리
- 용어집: 경로 순회란 무엇인가 · IDOR란 무엇인가 · OWASP Top 10이란 무엇인가
- 가이드: 파일 업로드 취약점
- 용어집: CVE란 무엇인가