루프 조작(Loop Bound Injection)
Loop Bound Injection
설명
루프의 종료 조건에 사용자 제어 객체의 length 값을 그대로 사용하는 경우, 공격자가 length를 비정상적으로 크게(예: 1e12, 1e100) 설정해 매우 긴 반복이나 사실상 무한 루프를 유발할 수 있습니다. 이로 인해 CPU/메모리 자원이 고갈되어 서비스 거부(DoS)가 발생할 수 있습니다. 예를 들어, 배열을 기대하는 코드에 { length: 1000000000000 } 같은 객체를 전달하면 for/while 루프가 끝나지 않거나 매우 오래 실행됩니다.
잠재적 영향
Denial of Service (서비스 거부): 과도한 반복으로 요청 처리가 지연되거나 서버가 응답 불가 상태가 될 수 있습니다.
CPU 자원 고갈: 루프가 장시간 실행되어 CPU를 점유, 다른 요청 처리에 영향을 줍니다.
메모리 고갈: 루프 내에서 push/concat 등 메모리 증가 작업이 수행되면 프로세스 메모리가 급격히 커져 크래시가 발생할 수 있습니다.
성능 저하/타임아웃: 이벤트 루프가 막혀 전체 서비스 응답 시간이 급격히 증가하고, 상위 레이어(로드밸런서/게이트웨이)에서 타임아웃이 발생할 수 있습니다.
해결 방법
length 사용 전 상한선 적용: const n = Math.min(Number(obj.length) || 0, MAX_ITEMS); 처럼 숫자로 변환 후 최대값을 제한하세요.
타입 검증: Array.isArray(obj)로 실제 배열인지 확인하고, 아니면 에러를 반환하거나 조기에 종료하세요.
정규화된 컬렉션으로 변환: 필요한 경우 Array.from(obj).slice(0, MAX_ITEMS)로 안전한 배열로 변환 후 순회하세요.
안전한 반복문 패턴: for (let i = 0; i < n; i++) 또는 for...of로 사전 제한된 컬렉션만 순회하세요.
방어적 기본값: obj가 null/undefined이거나 length가 NaN/음수면 0으로 취급하세요.
취약한 코드 및 안전한 코드 예시
취약한 코드
안전한 코드
설명:
취약한 코드: 요청 본문의 items.length 값을 그대로 루프 종료 조건에 사용합니다. 공격자가 items에 { length: 1e12 } 같은 객체를 보내면 루프가 1조 회 이상 반복되어 CPU가 고갈되고 서비스 거부(DoS)가 발생할 수 있습니다.
안전한 코드: 반드시 Array.isArray로 실제 배열인지 확인하고, length를 숫자로 변환한 뒤 합리적인 상한(MAX_ITEMS)을 적용합니다. 또한 slice로 잘라낸 제한된 컬렉션만 순회하여 과도한 반복을 원천 차단합니다.
참조
Last updated