방어선 2-①: WAF와 OWASP CRS가 잡는 것, 못 잡는 것

아프로·2026년 10월 30일

방어선 2-① WAF와 OWASP CRS 표지

WAF(Web Application Firewall)는 앱 앞에서 HTTP 요청을 보고 공격처럼 생긴 것을 막는 장치입니다. 1~6편이 앱 안의 이야기였다면, 이번 글부터는 앱 앞입니다. 결론부터 말하면 WAF는 요청 하나만 봐도 이상한 것을 잘 잡고, 누구의 것인지 알아야 하는 것은 못 잡습니다. 둘을 구별해서 기대해야 실망하지 않습니다.

OWASP CRS란

OWASP CRS(Core Rule Set)는 ModSecurity 호환 WAF에 넣어 쓰는 공개 규칙 묶음입니다. SQL 주입, XSS, 경로 조작(LFI), 원격 명령 실행, 프로토콜 위반, 알려진 스캐너 같은 범용 공격 범주를 겨냥합니다. 2026-10-30 기준 최신 릴리스는 v4.30.0(2026-10-02)이고, 장기 지원(LTS) 줄은 v4.25.x입니다.

CRS를 돌리는 엔진은 둘이 대표적입니다.

  • ModSecurity. 가장 오래된 엔진입니다. Trustwave의 후원이 2024년 7월 1일로 끝났고, 2024년 1월 OWASP 프로젝트로 옮겨 왔습니다. v2는 Apache 모듈, v3(libmodsecurity)는 Nginx 같은 서버에 커넥터로 붙입니다.
  • Coraza. Go로 쓴 OWASP 프로젝트입니다. CRS v4와 100% 호환을 내세웁니다. Caddy, Envoy(proxy-wasm), HAProxy에 붙이는 연결 프로젝트가 있습니다.

이상 점수와 패러노이아 레벨

규칙이 맞을 때마다 점수가 쌓이고 기준 5를 넘으면 막히는 이상 점수 방식과, PL1에서 PL4로 갈수록 규칙이 늘어나는 그림

CRS는 규칙 하나가 맞았다고 바로 막지 않습니다. 이상 점수(anomaly scoring) 방식입니다.

  • 규칙이 맞을 때마다 점수가 쌓입니다. 심각(CRITICAL) 5점, 오류(ERROR) 4점, 경고(WARNING) 3점, 알림(NOTICE) 2점.
  • 요청의 점수 합이 들어오는 쪽 기준값(기본 5) 이상이면 막습니다. 나가는 쪽(응답)은 기본 4입니다.

패러노이아 레벨(PL)은 얼마나 깐깐하게 볼지입니다. 레벨이 오를수록 규칙이 더해집니다.

레벨CRS 문서의 설명
PL1기본. 오탐이 드물어 인터넷에 열린 어떤 서버에도 맞음
PL2실제 사용자 데이터를 가진 사이트(예: 쇼핑몰). 조금 조정이 필요
PL3보안 요구가 높은 사이트(예: 온라인 뱅킹). 오탐이 예상되고 제외 규칙이 필수
PL4거의 모든 공격을 잡지만 정상 트래픽도 많이 걸림. 많은 조정이 필요

막는 레벨(blocking)과 탐지 레벨(detection)을 따로 둘 수 있습니다. 예를 들어 PL1로 막으면서 PL2 규칙은 기록만 하게 해 두면, PL2로 올리기 전에 오탐을 미리 볼 수 있습니다. 6편의 섀도 모드와 같은 생각입니다. 공식 Docker 이미지에서는 BLOCKING_PARANOIA, DETECTION_PARANOIA 환경 변수로 정합니다.

오탐 조정의 원칙

CRS 문서가 권하는 방식은 이렇습니다.

  • CRS 파일은 고치지 않습니다. 업데이트 때 덮어써집니다.
  • 제외 규칙(rule exclusion)을 따로 씁니다. 설정 시점 제외(SecRuleRemoveById, SecRuleUpdateTargetById 등)는 CRS 뒤 파일에, 실행 시점 제외(ctl:ruleRemoveTargetById 등)는 CRS 앞 파일에 둡니다.
  • 가장 좁게 제외합니다. 규칙 전체를 끄기보다, 특정 경로의 특정 파라미터 하나만 빼는 식입니다.
  • WordPress, Drupal, Nextcloud처럼 흔한 앱은 미리 만든 제외 플러그인이 있습니다.

한국어 서비스라면 특히 확인할 것이 있습니다. 높은 레벨에는 아스키 밖 문자를 의심하는 규칙이 있습니다. 한국어 검색어가 이런 규칙에 걸리는지 섀도로 먼저 보세요. 15편에서 재 보니 PL3부터 규칙 920272 하나로 한국어 검색 요청이 막혀, 정상 사용자 150명 중 112명이 403을 받았습니다.

WAF가 잘 잡는 것

요청 하나만 봐도 이상한 것입니다.

  • 쿼리나 본문에 SQL 문법, 스크립트 태그, ../ 같은 경로 조작, 셸 명령 조각이 들어 있다.
  • HTTP 규칙을 어긴 요청(이상한 메서드, 잘못된 헤더, 너무 긴 인자).
  • 알려진 스캐너의 User-Agent.

이런 공격은 앱 코드의 빈틈 하나만 있어도 큰 사고가 되므로, 앞에서 한 번 거르는 가치가 큽니다. 새 취약점이 알려졌을 때 코드를 고치기 전 가상 패치를 거는 자리이기도 합니다.

WAF가 못 잡는 것

맥락이 있어야 아는 것입니다.

  • IDOR. GET /api/orders/100458은 아무 공격 문자열도 없는 깨끗한 요청입니다. 주인이 누구인지는 DB만 압니다.
  • 느린 열거. 90초에 한 번 오는 깨끗한 요청을 이상하다고 볼 근거가 없습니다.
  • 업무 논리 악용. 쿠폰을 순서를 바꿔 여러 번 쓰는 것, 장바구니 가격을 정상 범위 안에서 바꾸는 것.
  • 정상 문법의 퍼징. 모르는 파라미터 이름(debug=1, role=1)은 문자열로는 평범합니다. 스키마를 아는 앱만 "이건 없는 이름"이라고 압니다.

그래서 원문의 "WAF + RASP" 조합 자체는 맞습니다. 다만 IDOR과 열거는 RASP라는 제품보다 우리 코드의 인가 이벤트로 잡는 것이 정확합니다(3편).

시험은 어떻게 하나

CRS는 규칙마다 공개 회귀 시험을 함께 둡니다. tests/regression/tests 폴더에 규칙 ID별 YAML이 있고, go-ftw라는 도구로 돌립니다. 이 시험은 "엔진과 규칙이 설계대로 반응하는가"를 확인합니다. 운영 환경에서 공격을 몇 % 막는가와는 다른 질문입니다.

이 시리즈는 두 가지를 직접 돌렸습니다(15편).

  • 이 맥에서 ModSecurity v3 + CRS 회귀 시험 5,221개: 5,221개 통과. Coraza로는 5,218개 중 5,189개 통과, 29개 실패(원인 확인 필요).
  • 실험실 앱 앞에 ModSecurity + CRS 4.30.0을 두고, 같은 IDOR·퍼징·정상 트래픽을 PL1~4로 흘려 무엇이 막히는지. 교과서 공격 표본은 PL1부터 10/10 막았고, IDOR 요청 660건은 PL4까지 하나도 막지 않았습니다.

정리

  • WAF와 OWASP CRS는 주입 문자열, 프로토콜 위반, 스캐너처럼 요청 하나로 판단할 수 있는 공격에 강합니다.
  • CRS는 이상 점수(기본 기준 5)로 막고, 패러노이아 레벨로 깐깐함을 정합니다. 탐지 레벨을 막는 레벨보다 높게 두고 오탐을 먼저 보세요.
  • 오탐은 CRS를 고치지 말고 가장 좁은 제외 규칙으로 다룹니다.
  • IDOR, 느린 열거, 업무 논리, 정상 문법의 퍼징은 WAF가 볼 수 없습니다. 그건 앱의 몫입니다.

출처와 확인 날짜

모든 링크는 2026-10-30에 확인했습니다.


이전 글 방어선 1-④: 단계적 대응과 섀도 모드
다음 글 방어선 2-②: IP 평판·위협 인텔리전스의 쓸모와 한계
시리즈 전체 목록 해킹 시도를 몇 초 안에 잡는 서버 전체 목록

profile
고인물 개발자(OG)

0개의 댓글