CSP 기본 개념
Content Securty Policy 즉, CSP라 불리는 콘텐츠 보안 정책은 웹 애플리케이션에 대한 클라이언트 측 보안 정책이다.
웹 페이지에서 로드되거나 실행될 수 있는 리소스의 출처를 제어함으로써 XSS를 포함한 다양한 클라이언트 측 코드 삽입 공격을 방어하는 추가 보안 계층이다. 서버가 HTTP 응답 헤더에 CSP 규칙을 지정하면 브라우저는 해당 규칙에 따라 다양한 콘텐츠를 어떤 출처에서 불러올 수 있는지를 제한하게 된다. CSP 규칙에 위배되는 행위가 발생하면 브라우저 콘솔에는
"Refused to execute inline script because it violates the CSP directive…"와 같은 오류가 표시된다.
즉, CSP는 신뢰된 출처로 명시된 리소스만 로드/실행하고 나머지는 모두 차단하는 화이트리스팅 모델이다.
하지만 이는 2차 보호막 역할일 뿐 입력 검증이나 코딩상의 보안 조치를 대체하지는 않는다.
서버/클라이언트 상호작용 구조
CSP는 기본적으로 서버와 브라우저가 상호작용한다.
웹 서버는 HTTP 응답 헤더에 Content-Security-Policy를 설정하면 브라우저는 이를 해석해 해당 페이지에 적용한다.
Content-Security-Policy: default-src 'self'; img-src 'self' safe-website.com; style-src 'self';
위 헤더를 예시로 들어보자.
다양한 리소스의 출처를 자기자신인 self로 지정해놓고 img 리소스에 대한 "safe-website.com"도메인만을 허용했다.
이 때 브라우저는 페이지를 로드하면서 CSP 헤더에 명시된 정책 문자열을 파싱하여 지시어 별로 허용된 출처 목록을 지정하게 된다. 이러한 CSP 정책 구문은 directive value 쌍으로 우리어지며 세모콜론(;)으로 구분된 여러 지시어들의 집합이다.
각각의 디렉티브는 특정한 유형의 리소스를 의미하고 그 값으로 해당 리소스에 대해 허용할건지에 대한 값을 지정한다.
이러한 디렉티브를 참고하여 브라우저는 콘텐츠를 불러오거나 허용 여부를 검사하게 된다.
script-src : 'self'와 같이 되어있을 경우 <script> 혹은 <img>를 통한 스크립트 삽입시 인라인 스크립트는 브라우저에 의해 CSP오류 메시지와 함께 실행되지 않는다. 이러한 거부 동작은 브라우저 엔진 수준에서 이루어지게 된다.
CSP의 주요 디렉티브와 작동 원리
CSP의 각 리소스 타입별 디렉티브를 제공하며, 이를 통해 세부적으로 통제한다고 설명했다.
이러한 주요 CSP 디렉티브와 그 동작 원리를 알아보겠다.
- default-src
- 다른 지시어를 따로 지정하지 않은 경우의 기본 저책으로 default-src 'self'의 경우 모든 리소스는 자신의 출처에서만 로드할 수 있도록 한다. 혹은 default-src 'none' 처럼 엄격하게 둘 수도 있다.
- script-src
- 자바스크립트의 소스를 제어하며, 외보 스크립트 파일의 허용 도메인을 지정할 수 있다. 혹은 인라인 스크립트나 eval같은 동적코드 실행 여부가 결정된다. unsafe-inline 옵션을 주지 않는다면 일반적인 인라인 코드와 다양한 이벤트 핸들러 내 스크립트는 기본적으로 차단된다. 이와 같은 형식으로 unsafe-eval을 주지 않을 경우 동적 생성 코드함수도 실행이 금지된다.
- style-src
- css 요소의 허용 소스를 정의하며, style-src 'self' 'unsafe-inline'과 같이 설정하면 외부 스타일시트는 자기 자신에서만 불러오며, 태그 혹은 속성에 의한 인라인 CSS도 허용한다.
- img-src
- 이미지 소스를 정의하며, img-src 'self' data:로 설정하면 현재 출처의 이미지와 data: URI 스키마를 통한 이미지가 로드된다.
- connect-src
- AJAX호출, WebSocket, EventSource 등 동적연결을 수립하는 대상의 범위를 지정한다. connect-src 'self' api.exampl.com으로 설정할 경우 현재 사이트 및 api.example.com으로의 XHR/Fetch, WebSocket 연결만 허용되고 다른 호스트로의 백그라운드 요청은 막힌다.
- font-src
- @font-face 파일의 소스를 제어하며, 폰트 파일을 로드할 신뢰 출처를 지정한다.
- media-src
- 비디오, 오디오 등 미디어 콘텐츠의 허용 출처를 지정 <audio>, <video> 태그가 사용된다.
- object-src
- <object>,<embed>등을 통해 임베드하는 플러그인 콘텐츠(Flash, PDF등)의 소스를 제한하며, 현대 웹 사이트에서는 플러그인을 보통 사용하지 않아 none로 설정하지만 그렇지 않은 경우 <object data="data:ext/html; base64,...">와 같이 우회공격이 가능하다.
- frame-ancestors
- <iframe>,<frame> 혹은 <object>로 삽입할 수 있는 상위 프레임의 출처를 제한한다. 보통 none를 통해 클릭재킹을 방어한다.
- form-action
- 현재 페이지의 <form> 제출 URL을 제한한다. form-action 'self' secure.exampl.com으로 설정하면 로그인이든 기타 폼 제출이 자기 자신의 도메인 혹은 지정 도메인으로만 전송된다.
- base-uri
- 현재 문서의 <base> 태그로 설정할 기본 URL을 제한한다. self 혹은 none으로 설정되지 않은 경우 <base href=',,,'>태그를 악의적으로 삽입해 페이지의 상대 URL 해석 기준을 변경함으로 써 XSS공격 등이 가능하다.
이렇게 각 디렉티브에 대한 정책을 기반으로 허용과 차단을 수행한다. 핮만 Nonce 및 해시 기반은 예외 허용이 가능하다.
CSP Level 2부터 도입된 nonce의 경우 script태그 내에 nonce 속성을 부여하여 해당 nonce를 지닌 인라인 스크립트만 예외적으로 실행할 수 있다 또한 시의 경우도 인라인 코드에 SHA-256등의 해시값을 구해 CSP 태그 내에 sha-256...<hash>형태로 지정하여 해시와 일치하는 스크립트 코드만 실행할 수 있다.
CSP 우회
CSP 설정이 미흡하거나, 잘못 설정될 경우 이를 충분히 우회할 수 있다.
호스트 기반의 화이트리스트에 의존하는 전통적인 CSP 구성은 대부분 설정에서 우회 가능성이 존재한다.
'unsafe-inline' 허용으로 인한 위험
insafe-inline을 존재하는 경우 사실상 인라인 스크립트를 허용하므로 CSP가 XSS방어 목적으로는 활용되지 못한다.
이럴 경우
Content-Security-Policy: script-src https://trusted.cdn.com 'unsafe-inline';
위와 같은 CSP 정책이 있는 경우 CDN에서 넘어온 스크립트와 인라인 코드를 모두 허용하라는 뜻으로, <script>태그를 차단하지 않고 실행한다.
'unsafe-eval' 허용으로 인한 위험
unsafe-eval 옵션이 허용될 경우 eval() 혹은 new Function 등을 통한 동적 코드 실행이 가능해진다.
특히, JSON 등 데이터를 처리하는 과정에서 eval 을 사용하도록 허용할 경우, 외부 입력을 통한 스크립트를 실행시킬 수 있게 된다.
기존의 브라우저의 경우 eval() 호출은 CSP 위반으로 가주하지만, unsafe-eval 이 존재할 경우 eval로 생성한 코드도 실행된다.
Content-Security-Policy: script-src https://trusted.cdn.com 'unsafe-eval' data: http:;
위와 같은 CSP 정책이 있는 경우 data: 스킴을 사용하여 URI로 포함하여 페이로드 작성이 가능하다.
<script src="data:text/javascript;base64,YWxlcnQoJ1hTUycp"></script>
이 경우 CSP정책상 data: 로 시작하는 스크립트도 허용되므로 브라우저단에서는 해당 스크립트를 로드하여 디코딩 후 실행하게 된다. 이 과정에서 eval과 유사한 동적 코드 실행이 일어나게 된다. 보통의 경우 unsafe-eval이 없다면, 브라우저는 data URI 스크립트를 해석하는 과정에서 차단하는 경우가 존재한다.
JSONP 및 신뢰된 제 3자 도메인으로 인한 위험
특정된 제 3자 도메인을 script-src로 허용하는 경우 해당 도메인의 취약한 JSONP 엔드포인트 등을 악용한 공격이 가능하다.
JSONP는 크로스 도메인 데이터를 주고받기 위한 오래된 기법으로, <script> 태그를 이용해 원격에서 JSON 데이터를 호출하는 방식이다. 이 경우 JSONP 요청시 callback이라는 파라미터로 자바스크립트 함수를 지정할 수 있는데, 취약한 JSONP 엔드포인트는 해당 파라미터 값을 그대로 응답에 반영하게 된다.
Content-Security-Policy: script-src 'self' https://api.trusted.com; object-src 'none';
위와 같은 CSP 정책이 있는 경우 이는 자체 도메인과 apo.trusted.com 에서 온 스크립트를 허용하고 플러그인 객체는 금지한다는 의미이다. api.trusted.com 도메인의 https://api.trusted.com/search?query=test&callback=call 와 같은 엔드포인트가 존채하고, callback 파라미터 값에 따라 응답이 call({...JSON data...}) 형태로 반환 될 경우 callback에 JavaScript 구문을 넣어 호출시킬 수 있다.
<script src="https://api.trusted.com/search?query=check&callback=alert#1337"></script>
이러한 방법은 과거 구글의 JSONP API 등이 자주 악용되었으며, 유명 소셜미디어 플랫폼의 과도하게 널널한 CSP로 인해 이를 악용된 사례도 존재한다.
프레임워크를 통한 CSP 정책상 위험
AngularJS 1.x와 같은 일부 프론트엔드 프레임워크는 자체적인 표현식(expression)이나 sandbox 우회 취약점이 존재한다.
CSP가 설정되어 있더라고 특정 우회를 통해 XSS를 일으킬 수 있다고 알려져 있다. 특히 AngujlarJS 1.0.8등 초기 버전은 CSP 호환 모드가 있지만 여전히 스크립트 가젯(SCRIPT GADGET)을 이용하면 악의적인 동작이 가능하다.
AngularJS 1.x 버전에서는 $event 객체를 사용해 글로벌 window에 접근하는 등의 CSP 우회 페이로드들이 존재한다.
공격자는 취약 페이지에 AngularJS모듈을 로드하고 특정 지시자/속성을 삽입하여 악성 코드를 실행시킨다.
예를 들어, 특정 사이트의 CSP 정책이 ajax.googleapis.com 에서의 AngularJS로딩을 허용할 경우
아래와 같은 페이로드를 작성할 수 있다.
<div ng-app ng-csp ng-click="$event.view.alert('XSS')"></div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
AngularJS 1.0.8을 불러온 뒤 ng-app 디렉티브로 Angular 어플리케이션을 초기화한다.
그 후 ng-click에 $event.vuew.alert('xss')를 지정 이후 해당 페이지에서 사용자가 해당 <div>를 한 번 클릭하면 $event.view는 전역 window객체를 가리키므로 window.alert('xss')를 호출하게 된다. 즉, CSP가 인라인 스크립트를 막더라도 AngularJS 프레임워크를 통해 이벤트 속성 내에 자바스크립트를 호출하여 우회할 수 있다.
파일 업로드를 통한 자체 스크립트 삽입 위험
script-src 'self'와 같이 자기 자신 도메인만 허용하는 등 안전힌 것 같은 정책이더라도,
해당 도메인에 파일 업로드 기능이 존재하고 해당 기능에서 파일업로드 취약점이 있을 경우, 공격자는 자바 스크립트 파일을 올린 뒤 해당 파일을 불러와 이를 우회할 수 있다.
오픈 리다이렉트(Open Redirect)로 인한 위험
화이트리스트에 여러 출처가 포함된 경우, 그 중 하나라도 Open Redirect 취약점이 존재한다면 이를 악용하여 CSP 우회가 가능하다. 예를 들어 허용된 도메인 A에서 리다이렉트를 시켜, 다른 허용된 도메인 B의 스크립트를 가져오게 함으로써 복합 우회가 가능하다.
Content-Security-Policy: script-src 'self' example.com/assets/ redirector.com; object-src 'none';
자기 자신과 example.com/assets/ 경로, 그리고 redirector.com 도메인을 허용했다.
공격자는 redirector.com 도메인에 오픈 리다이렉트 취약점을 악용하여 ' https://redirector.com/redirect?url=https://example.com/assets/evil.js' 와 같이 임의 URL로 리다이렉트 할 경우 evail.js가 존재하지 않는 경로지만, example.com에 JSONP 취약점이 있는 /assets/api 엔드포인트로 인해 evil.js 라는 이름으로 악의적인 응답을 돌려줄 수 있다고 가정한다. 그럼 아래와 같은 페이로드를 생성할 수 있다.
<script src="https://redirector.com/redirect?url=https%3A//example.com/assets/api%3Fcallback%3Dalert"></script>
허용된 두 출처 A와 B를 교묘히 결합하여 A에서 B로 리다이렉트되도록 함으로써 공격 흐름을 감출 수 있다.
즉, redirector.com 서버는 요청을 받으면 url 파라미터의 값으로 리다이렉트 시키며, Location 헤더가 https://example.com/assets/api?callback=alert 으로 설정되어 브라우저는 자동으로 example.com으로 재요청을 보낸다. 해당요청의 응답은 alert()를 호출하는 JSONP 스크립트 이므로, 최종적으로 악성 코드가 실행된다.
경로 지정으로 인한 위험 (url 인코딩 악용)
CSP 정책은 호스트뿐 아닌 경로까지 지정하여 스크립트를 제한할 수 있다. 예를 들어 script-src example.com/safe/처럼 특정 디렉터리 경로만 허용하는 경우 URL 인코딩으로 우회가 가능하다. '/' 문자의 경우 %2f와 같이 CSP는 경로로 인식하지만 서버 측에서는 디코딩되어 상위 디렉터리로 이동하는 식의 취약점이 존재한다.
Content-Security-Policy: script-src https://example.com/safe/
위와 같은 CSP 정책이 존재하는 경우 https://example.com/safe/ 경로 애라의 스크립트만 안전하다고 판단했지만,
공격자는 URL에 %2f를 사용해 우회를 시도한다. CSP 검사 시 %2f를 단순한 문자열로 취급하기 때문에
https://example.com/safe%2fmlicious.js 는 여전히 example.com/safe/ 경로로 인식된다. 그러나 웹 서버는 %2f를 디코딩 하여 실제 경로를 /safe/malicious.js로 해석한다. 이 외에도 일부 서버는 %2f..%2f 와 같이 url encoding를 악용하며 시퀀스를 허용한다. 즉, %2f..%2f는 서버 측에서 /../로 해석되어 /safe/../ 즉 루트 경로의 /unsafe/evil.js를 가리킨다. 이러한 경로 기반 우회는 모든 브라우저에서 일관되게 가능한 것으로 알려져 있다.
대응 방안
신뢰할 수 있는 출처만 허용
*(와일드카드)나 data:, blob: 등의 스킴을 무분별하게 사용하지 말고, 꼭 필요한 도메인만 지정, CND 허용시 호스팅된 라이브러리의 안정성 검사 및 서브도메인 전체 보다는 개별 도메인만 허용 또한 허용된 도메인 서비스에 JSONP/Open Redirect 등의 취약점 점검
인라인 스크립트/스타일 금지
unsafe-inline 옵션은 가급적 사용하지 않으며, 불가피할 경우 Nonce 혹은 해시 방식을 함께 사용
eval() 등 동적 코드 실행 금지
unsafe-eval 옵션은 가급적 사용하지 않아야함
모든 콘텐츠 유형에 대한 지시어 명시
default-src 'none'을 통해 각 필요 리소스만 개별 지시어로 허용
특수 지시어 활용
form-action, frame-ancestors, base-uri 등을 설정하여 클릭재킹이나 피싱 폼, base 태그 공격까지 대비
오브젝트/플러그인 완전 차단
Flash와 같은 구플러그인은 현대 웹에서 불필요하므로 object-src 'none' 및 base-uri 'none' 등으로 설정
HTTPS 강화 옵션 추가
사이트가 HTTPS를 사용 중이라면 CSP에 upgrade-insecure-requests를 추가하여 혼합 콘텐츠를 자동 업그레이드 및 block-all-mixed-content를 통해 HTTP리소스를 막을 수 있음
신뢰할 수 있는 타입 지정
능하다면 CSP Level3의 require-trusted-types-for 'script' 지시어를 사용해 DOM XSS를 더 강력히 예방할 수 있다.
Subresource Integrity(SRI) 적용
외부 CDN에서 불러오는 자바스크립트 혹은 CSS에 SRI 해시를 사용하여 해당 리소스의 해시 값을 HTML에 명시하여, 내용이 변조되면 브라우저가 로드를 거부하도록함
정기적인 정책 평가와 모니터링
CSP정책은 정기적으로 점검하고 업데이트 해야함.
참고자료
https://cyberar.io/blog/a-guide-to-csp-bypass-methods-for-xss-attacks#:~:text=Real,party%20domain
https://medium.com/@rana.adnanali/ambushed-by-angularjs-a-hidden-csp-bypass-in-piwik-pro-32d3bdf3d8dd
https://web.dev/articles/strict-csp?hl=ko#:~:text=%EC%9D%B4%20%ED%8E%98%EC%9D%B4%EC%A7%80%EC%97%90%EC%84%9C%EB%8A%94%20%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9C%BC%EB%A1%9C%20%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94%20%ED%98%B8%EC%8A%A4%ED%8A%B8,%ED%8E%98%EC%9D%B4%EC%A7%80%EA%B0%80%20XSS%EC%97%90%20%EB%85%B8%EC%B6%9C%EB%90%98%EB%8A%94%20%EA%B2%BD%EC%9A%B0%EA%B0%80%20%EB%A7%8E%EC%8A%B5%EB%8B%88%EB%8B%A4
https://payatu.com/blog/content-security-policy/#:~:text=If%20your%20CSP%20points%20to,directory%20to%20load%20scripts%20from
https://www.invicti.com/blog/web-security/content-security-policy/#:~:text=%2A%20%60script,lets%20you%20restrict%20image%20sources
https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#:~:text=Defense%20in%20Depth%C2%B6
https://www.imperva.com/learn/application-security/content-security-policy-csp-header/#:~:text=CSP%20allows%20server%20administrators%20to,and%20HTML%20event%20handling%20attributes
'Web > Web Hacking Techniques' 카테고리의 다른 글
Server Side Includes (SSI) Injection (0) | 2025.06.26 |
---|---|
LDAP Injection (Active Directory) (0) | 2025.06.25 |
XSS(Cross Site Script) mechanism (0) | 2025.06.07 |
Redis (Redis Injection) (0) | 2025.05.19 |
MongoDB (MongoDB Injection) (0) | 2025.05.16 |