
AWS WAF에서 /@username, /p/123 같은 경로만 안전하게 열려면, 경로 정규식을 좁게 쓰는 것보다 규칙 순서와 rate-based 규칙이 더 중요합니다. 이 글은 그 판단 과정을 정리한 것입니다.
DDoS 때문에 기본 액션을 Block으로 두고 필요한 것만 여는 웹 ACL에서, 아래 네 가지 경로 패턴을 국가·IP 조건과 상관없이 허용해야 했습니다.
/@{사용자명}/p/{ID}/v/{ID}/s/{ID}처음 든 질문은 두 가지였습니다. 와일드카드에 자릿수까지 제한을 걸면 더 안전한가, 그리고 이 경로를 Allow로 빼도 괜찮은가. 결론부터 말하면 자릿수 제한은 조금 도움이 되는 정도이고, 진짜 위험은 Allow 규칙이 이후 규칙을 통째로 건너뛰게 만든다는 점이었습니다.
/p/* 같은 식의 와일드카드 문법은 AWS WAF 규칙에 없습니다. 경로를 검사하는 방법은 크게 두 가지입니다.
/p/로 시작하는지(starts with)처럼 문자열 위치 조건으로 비교합니다. 대소문자를 구분하므로 필요하면 LOWERCASE 같은 text transformation을 겁니다.AWS 문서에 따르면 AWS WAF는 PCRE(libpcre)의 패턴 문법을 일부 예외와 함께 지원합니다. 문서에 명시된 미지원 항목은 다음과 같습니다.
\C, \R, \K 디렉티브zero-width assertion은 일부만 지원한다고 되어 있지만 정확한 범위를 적은 목록은 찾지 못했습니다. 지원되지 않는 문법을 쓰면 API가 오류를 반환하므로, 패턴은 저장 단계에서 검증됩니다.
정규식 패턴 세트에 패턴을 여러 개 넣으면 OR로 평가됩니다. 그래서 경로 형태마다 패턴을 나눠 넣는 방식이 괄호 없이 쓸 수 있어 안전합니다.
경로 정규식의 핵심은 앞뒤 앵커(^, $)와 허용 문자셋입니다. 실제 ID 형식에 맞게 고치세요. 아래는 예시입니다.
^/@[A-Za-z0-9_]{3,10}$
^/[pvs]/[A-Za-z0-9_-]{1,40}$
$ 앵커: 없으면 /p/123/../../admin 같은 경로도 앞부분만 맞으면 통과합니다.{3,10}처럼 서비스가 실제로 받는 범위와 같아야 합니다.하위 경로(/@abcde/contents, /@abcde/shorts)까지 한 줄로 쓰려면 선택 그룹을 쓰면 됩니다.
^/@[A-Za-z0-9_]{3,10}(?:/(?:contents|shorts|products))?/?$
이 패턴은 /@abcde, /@abcde/, /@abcde/contents, /@abcde/contents/를 모두 허용합니다. 끝 슬래시를 허용하지 않으려면 /?를 빼면 됩니다. (?:...)가 미지원 목록의 "capturing subexpression"에 해당하는지는 문서만으로 확정하지 못했습니다. 저장이 안 되면 괄호 없이 최상위 |로 풀어 쓰거나 패턴을 나눠 넣으면 됩니다.
^/@[A-Za-z0-9_]{3,10}/?$|^/@[A-Za-z0-9_]{3,10}/contents/?$|^/@[A-Za-z0-9_]{3,10}/shorts/?$|^/@[A-Za-z0-9_]{3,10}/products/?$
한글 등 다국어 사용자명을 허용한다면 퍼센트 인코딩된 값이 들어오므로 URL_DECODE 변환 후 문자셋에 해당 범위를 넣어야 합니다.
규칙의 검사 대상을 URI path로 지정하면 ? 뒤의 쿼리 스트링은 검사에 포함되지 않습니다. AWS 문서는 URL www.xyz.com/images/daily-ad.jpg?UserName=abc#section2를 예로 URI path(/images/daily-ad.jpg), 쿼리 스트링(UserName=abc), 프래그먼트(section2)를 별도 구성 요소로 나눠 설명합니다.
그래서 /@abcde?page=2나 /p/123?utm_source=x 같은 요청도 $ 앵커가 붙은 정규식에 매칭됩니다. 주의할 점이 두 가지 있습니다.
$ 때문에 쿼리가 붙은 요청이 전부 불일치합니다.문서에는 text transformation을 쓰지 않으면 AWS WAF가 URI를 정규화하지 않고 클라이언트가 보낸 그대로 검사한다고 되어 있습니다. 그래서 %2e%2e 같은 인코딩 우회를 막으려면 URL_DECODE를 걸어 두는 편이 안전합니다.
이 글에서 가장 중요한 내용입니다. AWS WAF의 Allow는 요청을 보호 대상 리소스로 넘기는 종료(terminating) 액션이고, 종료 액션이 실행되면 그 뒤 규칙은 평가하지 않습니다. AWS 모범 사례 가이드도 Allow 규칙은 뒤에 오는 규칙이 차단했을 요청까지 허용해 버릴 수 있어, 특히 상단에 둘 때 주의해야 한다고 설명합니다.
그러므로 /p/* 같은 경로를 Allow로 빼는 순간, 그 규칙보다 뒤에 있는 rate-based 규칙이나 Bot Control 같은 규칙은 해당 경로에 적용되지 않습니다. 유효한 형식의 경로로 대량 요청을 보내는 DDoS는 정규식으로 막을 수 없기 때문에, 자릿수를 맞춰도 그대로 통과합니다. 즉 정규식은 “어떤 요청이 후보인가”를 정하는 역할이고, 대량 요청 방어는 다른 규칙이 맡아야 합니다.
두 가지 설계 방향이 있습니다.
경로를 국가·IP 필터보다 앞에서 허용해야 한다면, Allow 규칙 바로 앞에 그 경로만 세는 rate-based Block 규칙을 두세요. rate-based 규칙은 scope-down 문장으로 추적·제한 대상 요청을 좁힐 수 있습니다. 문서는 기본 WCU를 2, 사용자 지정 집계 키 1개당 30을 더하고 scope-down 문장의 WCU는 따로 더하라고 설명합니다.
| 순서 | 규칙 | 액션 | 역할 | | --- | --- | --- | --- | | 1 | 악성 IP 차단 등 기존 방어 규칙 | Block | 알려진 나쁜 트래픽은 새 경로에 닿기 전에 차단 | | 2 | 경로 전용 rate-based 규칙 | Block | 4개 경로에 대해 IP당 요청 수 초과 시 차단 | | 3 | 경로 Allow 규칙 | Allow | 정규식과 GET 조건이 맞으면 허용, 이후 국가 필터를 건너뜀 | | 4 | 국가·IP 필터 등 나머지 규칙 | — | 그 외 요청 처리 | | 기본 | 웹 ACL 기본 액션 | Block | 어느 규칙에도 맞지 않으면 차단 |
2번이 3번 앞에 있다는 점이 핵심입니다. 순서가 바뀌면 이 경로는 rate 제한을 통째로 건너뀝니다. 기존 전역 rate 규칙이 뒤에 있어도 이 경로에는 적용되지 않으므로, 경로 전용 rate 규칙이 유일한 flood 방어선이 됩니다. 임계치는 이 경로의 정상 트래픽 로그(예: IP당 최대 요청 수)를 보고 정하세요. 문서만으로 정할 수 있는 값이 아닙니다.
운영 중인 웹 ACL에 규칙을 바로 Allow·Block으로 넣으면 정상 요청이 막히거나 의도하지 않은 경로가 열릴 수 있습니다. 아래 순서를 권합니다.
URL_DECODE를 설정합니다.이 글의 내용 중 문서로 확인하지 못한 부분은 아래와 같습니다. 실제 적용 전에 직접 확인해 보세요.
(?:...)가 미지원 목록에 걸리는지, 지원되는 zero-width assertion의 범위는 문서만으로 확정하지 못했습니다.참고 문서: