해당 환경은 WAF 실습을 위해 직접 제작했음을 밝힙니다.
해당 페이지의 취약점을 분석하기 위해 OWASP ZAP을 활용해 취약점 진단을 실시했습니다.
2025.10.23 - [Web/기초] - OWASP ZAP 설치 및 사용법(Active Scan)
OWASP ZAP 설치 및 사용법(Active Scan)
OWASP ZAP의 설치 방법과 Acrive Scan을 사용법을 알아보겠습니다. OWASP ZAP(Zed Attack Proxy)은 웹 애플리케이션의 보안 취약점을 찾는 데 사용되는 무료 오픈 소스 웹 애플리케이션 보안 스캐너입니다.1. O
luckyd8.tistory.com

테스트 시행 결과를 살펴보면 WAF를 적용했음에도 취약점이 존재하는 것을 확인할 수 있습니다.
1. Content Security Policy (CSP) Header Not Set
콘텐츠 보안 정책(CSP) 헤더가 설정되지 않았다는 취약점입니다.
Content Security Policy(CSP)는 웹 페이지에서 실행 가능한 콘텐츠(스크립트)를 정의하여 사이트를 보호하는 보안 정책입니다.
브라우저 보안 정책으로, 서버가 응답할 HTTP 헤더를 내려보내면, 브라우저가 이 정책을 강제함
WAF로 못 잡는 XSS 페이로드 실행을 브라우저 단게에서 차단 가능
| WAF | CSP |
| - WAF 서버 앞단에서 요청을 검사/차단 - 단순히 서버 사이드 공격을 방지 |
- 브라우저에서 실행되는 자바스크립트 행위를 제어 |
1.1 실습 - XSS
WAF 적용 전에는 당연하게도 XSS 공격이 성공적으로 실행됩니다.




하지만 WAF를 적용한 후에는 Nginx 서버가 403 Forbidden 응답을 반환하며, WAF에 의해 XSS 공격이 실패한 것을 확인할 수 있습니다.


1.2 WAF 우회
이를 우회하기 위한 방안으로는 이중 인코딩을 활용할 수 있습니다.
인코딩은 웹에서 사용하는 특수 문자를 안전하게 전달하기 위해 변환하는 방법을 말합니다. 이중 인코딩은말 그대로 이를 두 번 인코딩하는 것입니다.
일부 (취약점을 가진) WAF가 한 번만 디코딩을 하도록 설정되어 있다면 (현재 실습 환경 또한 그러함) 2차 인코딩 문자열을 1차 인코딩 문자열로 디코딩하게 됩니다. 1차 인코딩한 문자열은 분명히 위험한 스크립트가 포함된 문자열로 해석되지 않으므로, 해당 허용된 문자열을 웹 서버로 보내게 되고 이를 웹 서버에서 한 번 더 디코딩하면, 원본 스크립트가 반영되어 공격에 성공하게 됩니다.

혼합 인코딩 방법
본 실습에서는 URL 인코딩을 사용했지만, 그 밖에도 HTML Entity, Base64, 문자 등 다양한 인코딩이 존재합니다.
또한 이는 혼합해 사용할 수도 있습니다. 단일 인코딩 방식만으로는 탐지하기 어려운 복잡한 패턴을 만들어 WAF가 탐지하지 못하도록 하는 것이 이중 인코딩 공격의 핵심입니다.

이와 같은 이중 인코딩 코드를 삽입하면 성공적으로 XSS가 성공한 것을 확인할 수 있습니다.



1.3 WAF 우회 방지
WAF의 기본적인 보안 방법은 성공한 우회 코드를 성공하지 못하도록 하는 코드를 작성해 규칙을 추가하는 방식입니다.
1) 이중 인코딩 디코딩 규칙 추가
이중 인코딩이 성공했으니, WAF 설정을 수정해 모든 입력값을 여러 번 디코딩 하도록 규칙을 설정합니다.
SecRule ARGS|REQUEST_BODY
"(%253c|%253e|%2522|%2527|%253b|%2528|%2529|%252f|%255c)" \
"id:1001,phase:2,t:urlDecodeUni,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,\
msg:'Double Encoded XSS or Path Traversal Attempt',deny,log,status:403"
코드 분석
SecRule ARGS|REQUEST_BODY
"(%253c|%253e|%2522|%2527|%253b|%2528|%2529|%252f|%255c)" \
"id:1001,phase:2,t:urlDecodeUni,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,\
msg:'Double Encoded XSS or Path Traversal Attempt',deny,log,status:403"
■ t:urlDecodeUni,t:urlDecodeUni
이중 인코딩 된 URL을 디코딩하도록 지시하는 ModSecurity 변환 함수, 두 번 연속으로 작성해 두 번 디코딩 하도록 지시
■ "(%253c|%253e|%2522|%2527|%253b|%2528|%2529|%252f|%255c)"
이중 인코딩된 위험 문자를 탐지하기 위한 정규 표현식, 디코딩을 통해 변환된 문자가 해당 정규 표현식 속 문자에 포함된다면
■ deny,log,status:403"
WAF 서버(Nginx 서버)가 403을 반환
2) CSP 적용
XSS를 막기 위한 가장 근본적인 해결 방법입니다.
Content Security Policy (CSP)는 웹 페이지에서 실행 가능한 콘텐츠(스크립트)를 정의하여 사이트를 보호하는 보안 정책입니다. 서버가 응답할 HTTP 헤더를 내려보내면, 브라우저가 이 정책을 강제합니다.
WAF는 서버 앞단에서 요청을 검사하고 차단하는 서버 사이드 공격인 반면, CSP는 브라우저에서 실행되는 행위 자체를 제어하는 클라이언트 사이드 공격 방지를 수행합니다. 즉, WAF로 잡지 못하는 XSS 페이로드 실행을 브라우저 단계에서 차단 가능합니다.
| WAF | CSP |
| - WAF가 서버 앞단에서 요청을 검사/차단 - 서버 사이드 공격 방지 |
- 브라우저에 실행되는 자바스크립트 행위를 제어 - 클라이언트 사이드 공격 방지 |
2. 실습 - Union SQL Injection
2.1 실습 - Union SQLi
다음 실습은 Union SQL Injection을 활용한 실습입니다.
이번에도 역시 WAF 적용 전에는 SQLi 공격이 잘 처리되어 DB 데이터가 탈취된 모습을 확인할 수 있습니다.

WAF를 적용 후에는 Nginx 서버가 403 Forbidden 응답을 반환하며, WAF에 의해 SQLi 공격이 실패한 것을 확인할 수 있습니다.

2.2 WAF 우회
이를 우회하기 위한 방법으로 HTTP 파라미터 오염을 시도할 수 있습니다.
HTTP 파라미터 오염 (HPP, HTTP Parameter Pollution)은 공격자가 동일한 이름의 HTTP 매개변수(파라미터)를 여러 개 포함하여 WAF의 동작을 조작하는 공격 기법입니다.
이는 WAF와 웹 애플리케이션의 중복 파라미터 처리 방식의 차이점을 악용합니다.
예를 들어, 다음과 같은 공격문을 주입한다고 합시다.
http://localhost/search?query=WAF&query=' UNION SELECT 1, id, 3, pass, 5 FROM waf.user #
만약 WAF가 첫 번째 변수만 검사한다면 query = WAF는 정상이므로 통과합니다. 이후 웹 서버에서 마지막 값을 우선하거나, 중복되는 파라미터의 모든 값을 처리하는경우 두 번째 값 query = ' UNION SELECT ... 역시 허용되어 WAF 우회에 성공하게 됩니다.
실제로 해당 페이로드를 사이트에 넣어봤지만 결과는 여전히 WAF 서버의 403 Forbidden 응답을 반환 받았습니다.
실습 실패 원인은 처음 WAF를 구성할 때 추가한 OWASP CRS(Core Rule Set)에 있습니다. 이는 WAF의 오픈 소스 규칙 집합으로, WAF에서 사용하는 규칙 모음을 의미합니다.
이 규칙 속에는 HTTP 프로토콜 위반 탐지: REQUEST-921-PROTOCOL-ATTACK.conf 를 포함하는데, 이 규칙이 동일한 매개변수가 반복되는 것을 탐지해 비정상적인 것으로 간주해 차단하는 규칙입니다. 때문에 동일한 매개변수를 중복으로 사용한 해당 실습이 실패한 것으로 해석됩니다.
2.3 WAF 우회 방지
1) 동일 파라미터 정책
HPP 우회 공격을 방지하기 위해선 개발 단계에서 동일한 파라미터 처리 정책을 추가하는 방법이 있습니다.
해당 코드는 배열 여부를 확인해, 첫 번째 쿼리 값만을 사용하도록 해, 여러 쿼리의 허용을 방지합니다.
const query= Array.isArray(req.query.query) ? req.query.query[0] : req.query.query || '';
const search_keyword = `%${query}%`;
const sql = 'SELECT * FROM board WHERE title LIKE ?';
const [results] = await connection.execute(sql, [search_keyword]);
코드 분석
const query= Array.isArray(req.query.query) ? req.query.query[0] : req.query.query || '';
const search_keyword = `%${query}%`;
const sql = 'SELECT * FROM board WHERE title LIKE ?';
const [results] = await connection.execute(sql, [search_keyword]);
■ Array.isArray(req.query.query)
re.query가 배열인지 확인
클라이언트가 같은 이름의 파라미터를 여러 번 보낸 경우 배열이 발생
/search?query=WAF&query=' UNION SELECT ... → req.query.query === ['WAF', '' UNION SELECT ...']
■ req.query.query[0]
배열인 경우 첫 번째 쿼리 값을 선택, 동일한 파라미터가 여러 개일 경우 첫 번째 값만 사용하겠다는 정책
■ req.query.query || ''
배열이 아닐 경우, 일반적으로 파라미터가 하나만 전달된 경우 → 그대로 사용
|| '' → req.query.query가 undefined이거나 값이 비어 있는 경우 '' (빈 문자열)을 기본값으로 할당
2) 입력값 유효값 검사
혹은 애초에 사용자 입력값을 제한해 페이로드 작성을 제한하는 방법도 있습니다.
const whitelist = /^[가-힣a-zA-Z0-9\s]*$/;
if (!whitelis.test(query)) {
console.warn('허용되지 않은 문자:', query);
return res.status(400).send('잘못된 요청');
}
3) 보안 솔루션 도입 - WAF 강화
실습 실패의 원인이었던 동일한 매개변수를 차단하는 정책을 추가하거나 공격 예약어에 대한 인코딩 버전을 추가하는 등의 방식을 통해 WAF를 강화하는 것도 보안 방법이 됩니다.
'Web > 웹해킹' 카테고리의 다른 글
| [WAF] WAF(웹 방화벽)이란? Firewall, IPS와 차이, 동작 방식, 유형, 배치 (0) | 2025.10.23 |
|---|---|
| [Web] XSS를 이용한 웹 키로깅 공격 실습 (2) | 2025.07.21 |