1. 무한 반복 및 재귀의 위험성
이 취약점들은 프로그램이 특정 조건에서 종료되지 않고 계속해서 실행될 때 발생합니다.
-
CWE-835 (무한 루프): 반복문(for, while)의 탈출 조건이 결코 충족되지 않아 CPU 점유율을 100%까지 끌어올립니다. 이로 인해 해당 프로세스는 응답 불능 상태가 되며 전체 시스템의 성능이 저하됩니다.
-
CWE-674 (무한 재귀): 함수가 자기 자신을 끝없이 호출하여 스택 메모리를 모두 소모합니다. 이는 결국
StackOverflowError를 유발하며 애플리케이션을 강제로 종료시킵니다.
공격자는 조작된 입력값을 보내 특정 로직이 이러한 무한 루프나 재귀에 빠지게 함으로써 서비스 거부(DoS) 공격을 수행할 수 있습니다.
2. 흔히 발생하는 취약한 패턴
주로 외부 입력값을 루프의 조건이나 재귀의 깊이로 사용할 때 발생합니다.
-
부적절한 탈출 조건:
while(n != 0)상황에서n이 음수로 들어오거나 소수점이 발생하는 등 0에 도달할 수 없는 경우. -
재귀 깊이 제한 부재: 트리 구조나 연결 리스트를 탐색할 때, 순환 참조(Circular Reference)가 있음에도 이를 체크하지 않아 무한 호출에 빠지는 경우.
-
입력값 조작: 검색 알고리즘이나 수치 계산 로직에서 공격자가 매우 큰 값을 주입하여 연산이 끝나지 않게 만드는 경우.
3. 실무적 대응: 임계치 설정과 상태 검증
모든 반복 구조에는 '강제 종료 버튼'이 있어야 합니다.
-
최대 반복 횟수(Max Iterations) 설정: 루프가 예상보다 길어질 경우를 대비해 하드웨어 자원을 고려한 최대 반복 상한선을 설정합니다.
-
재귀 깊이(Recursion Depth) 제한: 재귀 함수 호출 시 현재 깊이를 카운트하여 특정 임계치를 넘으면 예외를 발생시키거나 중단합니다.
-
순환 참조 검사: 그래프나 트리 탐색 시 이미 방문한 노드를 기록하여 무한 루프를 방지합니다.
-
입력값 유효성 검사: 반복 횟수를 결정하는 입력값이 합리적인 범위 내에 있는지 사전 검증합니다.
4. CWE-835/674 대응 및 안전한 로직 구현 자바 코드 예시
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ResourceGuard {
private static final Logger logger = LoggerFactory.getLogger(ResourceGuard.class);
private static final int MAX_RECURSION_DEPTH = 50;
private static final int MAX_LOOP_LIMIT = 10000;
// [CWE-835 조치] 무한 루프 방지를 위한 최대 반복 횟수 제한
public void processDataWithLimit(int inputCount) {
int iterations = 0;
// 입력값이 비정상적일 경우를 대비해 이중 안전장치 마련
while (iterations < inputCount) {
if (iterations >= MAX_LOOP_LIMIT) {
logger.debug("Security Violation: Loop exceeded safety limit. Potential DoS attempt.");
break;
}
// 데이터 처리 로직...
iterations++;
}
}
// [CWE-674 조치] 무한 재귀 방지를 위한 깊이 제한
public void safeRecursiveCall(int depth) {
// 1. 탈출 조건 확인
if (depth > MAX_RECURSION_DEPTH) {
logger.debug("Security Violation: Max recursion depth reached. Terminating to prevent StackOverflow.");
throw new RuntimeException("Recursion depth limit exceeded");
}
// 2. 재귀 실행 (깊이 증가)
logger.debug("Executing recursive step at depth: {}", depth);
safeRecursiveCall(depth + 1);
}
}
코멘트: "언젠가 끝나겠지"라는 가정은 보안에서 매우 위험합니다. 특히 외부 시스템과 통신하거나 복잡한 수치 계산을 할 때는 반드시 **타임아웃(Timeout)**이나 최대 시도 횟수를 명시하십시오. logger.debug()를 통해 로직이 임계치에 도달했는지 모니터링하고, 시스템이 스스로를 파괴하는 루프에 빠지지 않도록 제어권을 유지하는 것이 가용성 보안의 핵심입니다.
댓글 달기