코드의 실행 시점과 상태 변화에 따라 발생하는 보안 약점을 **Java**와 **Python** 코드를 통해 학습합니다.
보안 검사 시점(Time of Check)과 실제 자원 사용 시점(Time of Use) 사이에 공유 자원의 상태가 악의적으로 변경되어 발생하는 취약점입니다.
/* 취약한 코드 (Java) */
import java.io.File;
// ... (생략)
File f = new File(fileName);
// 1. 검사 시점 (Time of Check)
if (f.exists() && f.canWrite()) {
// *** 공격자가 이 사이에 파일을 심볼릭 링크로 바꿔치기 ***
// 2. 사용 시점 (Time of Use)
FileWriter fw = new FileWriter(f, true); // 공격자의 파일에 쓰기 시도
fw.write(userData);
fw.close();
}
// 검사-사용 시점 차이로 인해 악의적인 파일에 쓰기 가능
/* 안전한 코드 (Java) */
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
// ... (생략)
Path path = Paths.get(fileName);
String data = "log data";
try {
// 파일 생성 및 쓰기를 원자적으로 처리 (파일이 존재하면 예외 발생)
Files.write(path, data.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
} catch (FileAlreadyExistsException e) {
// 파일이 이미 존재하면 TOCTOU가 발생하기 전에 실패 처리
System.out.println("파일이 이미 존재합니다.");
}
// CREATE_NEW 옵션이 원자성을 보장하여 TOCTOU 회피
# 취약한 코드 (Python)
import os
file_name = "shared_resource.tmp"
user_input = "malicious data"
# 1. 검사 시점 (Time of Check)
if os.path.exists(file_name):
# 공격자가 이 사이에 file_name을 심볼릭 링크로 바꿔치기
# 2. 사용 시점 (Time of Use)
with open(file_name, 'a') as f:
f.write(user_input + '\n')
# 검사 후 파일 열기 시점 사이에 상태가 변경될 위험
# 안전한 코드 (Python)
import os
file_name = "user_log.txt"
data = "log data"
try:
# os.O_EXCL 플래그: 파일이 이미 존재하면 에러 발생, 원자성 보장
fd = os.open(file_name, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
with os.fdopen(fd, 'w') as f:
f.write(data)
print("파일 쓰기 성공")
except FileExistsError:
# 파일이 이미 존재할 경우 안전하게 처리
print("파일이 이미 존재합니다.")
# os.open() 사용으로 Check와 Use를 결합
반복문이나 재귀함수에 종료 조건이 없거나 오류가 있어 시스템 자원을 고갈시켜 DoS 공격을 유발하는 취약점입니다.
/* 취약한 코드 (Java) */
public long calculateFactorialVulnerable(int n) {
// 종료 조건(Base Case)이 없거나 항상 참인 경우
return n * calculateFactorialVulnerable(n);
}
// 외부에서 이 함수를 호출할 경우 StackOverflowError 발생
/* 안전한 코드 (Java) */
public long calculateFactorialSecure(int n) {
if (n < 0 || n > 20) { // 1. 입력값 유효성 검사 및 오버플로우 방지
throw new IllegalArgumentException("Invalid input");
}
if (n <= 1) { // 2. 명확한 종료 조건(Base Case)
return 1;
}
return n * calculateFactorialSecure(n - 1); // 3. 상태가 종료 조건으로 수렴
}
# 취약한 코드 (Python)
def process_data_recursively(n):
# 종료 조건 누락
print(f"Processing item {n}")
process_data_recursively(n) # 무한 재귀 호출
# 호출 시 RecursionError (Python의 재귀 한계) 또는 무한 루프 발생
# 안전한 코드 (Python)
def process_data_secure(n, max_depth=50):
if n >= max_depth:
# 1. 최대 반복 횟수(깊이)를 제한
raise RecursionError("최대 처리 깊이 초과")
if n <= 1:
# 2. 명확한 종료 조건
return 1
return n * process_data_secure(n - 1)
# 또는 while 루프 사용 시 외부 입력에 의존하지 않는 안전한 탈출 조건 사용