정규식 기반 서비스 거부 (ReDoS)

Regular Expression Denial of Service (ReDoS)

설명

ReDoS는 정규식 엔진의 과도한 백트래킹을 유발하여 CPU를 점유하게 만들어 응답 지연 또는 서비스 중단을 일으키는 취약점입니다. 주로 사용자 입력을 그대로 정규식으로 컴파일하거나, 중첩 수량자 같은 위험 패턴을 사용했을 때 발생합니다. 공격자는 (a+)+$, (.*)+ 같은 패턴이나 매우 긴 입력을 보내 서버 스레드를 장시간 붙잡아 두어 자원을 소모시키고 서비스 거부 상태를 만들 수 있습니다.

잠재적 영향

  • 서비스 거부(DoS): 공격자가 조작한 정규식/입력으로 CPU를 고갈시켜 요청 처리가 지연되거나 중단됩니다.

  • 리소스 고갈: 다중 스레드 환경에서 워커 스레드가 정규식 평가에 묶여 연결 풀/스레드 풀이 고갈됩니다.

  • 가용성 저하 및 비용 증가: 지연 확대, 타임아웃 증가, 오토스케일링 비용 상승 등 운영 비용과 SLO가 악화됩니다.

해결 방법

  • 사용자 입력을 정규식으로 직접 사용하지 않기: 고정된 허용 목록(allowlist) 패턴만 사용하거나, 사용자 입력은 텍스트로만 취급합니다.

  • 입력 리터럴화: 사용자 입력을 패턴으로 쓸 필요가 없다면 Pattern.quote(input) 또는 Pattern.compile(input, Pattern.LITERAL)로 메타문자를 무력화합니다.

  • 위험 패턴 금지: 중첩 수량자 ((.*)+, (a+)+ 등), 광범위한 백트래킹 유발 패턴, 역참조(backreference) 등을 피하고, 수량자에 상한을 둡니다(예: \d{1,6}).

  • 길이/시간 제한: 검사 대상 문자열 길이를 제한하고, 별도 실행 컨텍스트에서 타임아웃을 적용합니다(스레드/Executor로 제한하거나, 타임아웃 가능한 엔진 사용).

  • 안전한 엔진 고려: 백트래킹이 없는 엔진 사용(re2j 등) 또는 사전 검증된 유틸을 사용합니다.

  • 사전 컴파일: 애플리케이션 시작 시 검토된 패턴만 미리 컴파일하여 재사용하고, 런타임 동적 컴파일을 피합니다.

  • 대체 로직: 단순 포함 여부/치환은 String.indexOf, String.replace 등 비-정규식 API로 대체합니다.

취약한 코드 및 안전한 코드 예시

취약한 코드

안전한 코드

설명:

  • 취약한 코드: replaceAll의 첫 번째 인자는 정규식입니다. 사용자가 제공한 문자열을 그대로 정규식으로 해석하면, (a+)+, (.*)+ 같은 패턴이나 긴 입력으로 엔진이 과도한 백트래킹을 수행해 CPU가 고갈되어 ReDoS가 발생할 수 있습니다.

  • 안전한 코드: - literal 모드에서는 Pattern.quote로 사용자 입력을 리터럴(일반 텍스트)로 처리하여 메타문자를 무력화하고 백트래킹 위험을 제거합니다.

  • 정규식이 꼭 필요하면, 사전에 검토된 허용 목록 패턴만 컴파일하여 위험한 동적 정규식 사용을 차단합니다. 수량자 상한 등 안전 제약을 통해 백트래킹 비용을 통제합니다.

참조

Last updated