#X-Content-Type-Options
#nosniff
#HTTP 헤더
#Content-Type 헤더
#MIME Type
#MIME형식
#시험페이지
#자바스크립트 파일 삽입 방어
MIME형식 해석 제한... X-Content-Type-Options: nosniff 헤더
Microsoft에서 제안하는 확장 헤더이며 웹서버가 보내는 MIME 형식 이외의 형식으로 해석을 확장하는 것을
제한하는 크로스사이트스크립트 방어법이다.
MS Internet Explorer 9부터 지원한다.
의미는 간단하다.
CSS MIME 형식("text/css ")을 가지지 않는 파일은 웹 브라우저가 CSS로 사용하지 않아야 한다.
JavaScript MIME 형식("text/javascript " 또는 "application/javascript ")을
가져야만 JavaScript로 해석해야 한다는 의미이다.
따라서 이 방어법은 브라우저 의존적이다.
글을 쓰는 현재 확인한 결과, 구글크롬(Google Chrome), MS IE 11, MS 엣지(MS Edge)는 지원하였고
파이어폭스(FireFox)와 사파리(Safari)는 지원하지 않았다.
참고할만한 바깥고리
웹서버가 설정해서 전송하는 Content-Type 헤더
웹서버는 HTTP 응답에 Content-Type: 헤더를 선언하여 자신이 보내는 내용이 어떠한 용도로
사용될 수 있는 지를 지정한다.
이 헤더에는 MIME(Multipurpose Internet Mail Extensions) 형식이 사용된다.
영어 이름에서 알 수 있듯이 원래는 메일 전송시 사용하던 인코딩 방식이다.
웹서버는 일반적으로 HTML 전송시에는 "text/html "이라는 MIME 형식으로 보낸다.
그외의 경우에는 대개 접근하는 파일의 확장자에 의해서 MIME 형식이 결정된다.
예를 들어 JPG 이미지는 "image/jpeg ",
CSS 파일은 "text/css ", MPG 파일은 "video/mpeg " 등과 같다.
확장자가 기존 MIME 형식에 정의되어 있지 않을 경우에는
텍스트 문서이면 "text/plain "이
이진 파일이면 "application/octet-stream "을 사용한다.
root@kali:~# curl -I http://www.daum.net/
HTTP/1.1 200 OK
X-UA-Compatible: IE=10
Expires: Sat, 01, Jan 1970 22:00:00 GMT
Pragma: no-cache
Cache-Control: no-cache, no-store, must-revalidate
P3P: CP="ALL DSP COR MON LAW IVDi HIS IVAi DELi SAMi OUR LEG PHY UNI ONL DEM STA INT NAV PUR FIN OTC GOV"
Content-Type: text/html;charset=UTF-8
Content-Language: en-US
X-UA-Device-Type: pc
Date: Sun, 20 Nov 2016 15:55:40 GMT
Connection: keep-alive
root@kali:~#
위의 예는 일반적인 웹 접속시 볼 수 있는 "Content-Type: text/html " 헤더이다.
다음(Daum) 웹서버는 "charset=UTF-8"을 덧붙여서 문자 인코딩 방식이 UTF-8이라는 것을 함께 지정하고 있다.
root@kali:~# curl -v http://search.daum.net/OpenSearch.xml
* Trying 180.70.134.60...
* Connected to search.daum.net (180.70.134.60) port 80 (#0)
> GET /OpenSearch.xml HTTP/1.1
> Host: search.daum.net
> User-Agent: curl/7.50.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 20 Nov 2016 13:34:28 GMT
< Server: Apache
< Last-Modified: Wed, 16 Nov 2016 07:25:54 GMT
< Accept-Ranges: bytes
< Content-Length: 716
< Vary: Accept-Encoding
< Connection: close
< Content-Type: application/xml
<
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:referrer="http://a9.com/-/opensearch/extensions/referrer/1.0/">
<ShortName>Daum</ShortName>
<Description>Kakao Corp provider</Description>
<Url template="http://search.daum.net/search?w=tot&DA=JU1&ie={inputEncoding}&src={referrer:source?}&q={searchTerms}" type="text/html"/>
<Url template="http://suggest.search.daum.net/sushi/opensearch/pc?q={searchTerms}&DA=JU2" type="application/x-suggestions+json"/>
<InputEncoding>UTF-8</InputEncoding>
<Image height="16" type="image/x-icon" width="16">http://search.daum.net/favicon.ico</Image>
* Closing connection 0
</OpenSearchDescription>l;root@kali:~#
위의 사례에서 .xml 확장자 파일을 접근했을 때는 웹서버가 "application/xml "라는
MIME 형식을 전송하였다.
root@kali:~# curl -I http://lithium.daum.net/resources/external/top/trendad.min.js
HTTP/1.1 200 OK
Date: Sun, 20 Nov 2016 13:43:15 GMT
Server: Apache
Accept-Ranges: bytes
ETag: W/"664-1476342908000"
Last-Modified: Thu, 13 Oct 2016 07:15:08 GMT
Content-Length: 664
Connection: close
Content-Type: application/javascript
root@kali:~#
위의 사례에서 .js 확장자 파일을 접근했을 때는 웹서버가 "application/javascript "라는
MIME 형식을 전송하였다.
일반적으로는 "text/javascript "로 보내는 경우가 더 많다.
root@kali:~# curl -I http://img.naver.net/static/www/u/2013/0731/nmms_224940510.gif
HTTP/1.1 200 OK
Server: Testa/4.8.6
Last-Modified: Wed, 31 Jul 2013 13:49:47 GMT
Accept-Ranges: bytes
Content-Length: 4368
Access-Control-Allow-Origin: *
Content-Type: image/gif
Cache-Control: max-age=26414865
Expires: Fri, 22 Sep 2017 07:32:42 GMT
Date: Sun, 20 Nov 2016 14:04:57 GMT
Connection: keep-alive
root@kali:~#
.gif 확장자를 가지는 파일은 웹서버가 "image/gif "라는 마임형식으로 응답한다.
MIME 형식의 엄격한 적용을 통한 보안위협 완화
웹서버가 보내는 MIME 형식을 이용하여 일부 보안 수준을 높이려는 시도가 바로
X-Content-Type-Options: sniff 헤더이다.
이 HTTP 헤더가 선언되면 - 지원하는 웹 브라우저의 경우에는 -
지정된 MIME형식 이외의 다른 용도로 사용하고자 하는 것을 차단한다.
예를 들어보자.
어떤 공격자가 .jpg 확장자의 파일을 웹 서버에 업로드했다고 하자 -
실제로 이 파일은 JPEG 이미지가 아니라 내용은 자바스크립트르 담고 있다.
이 파일을 웹브라우저로 접근하면 웹서버는 확장자가 jpg 이므로
image/jpeg 라는 MIME 타입을 반환한다.
공격자가 이 이미지 파일을 자바스크립트로 사용하려고 할 때 웹서버가
X-Content-Type-Options: sniff 헤더를 함께 전송하면
웹브라우저는 이 파일의 MIME 형식이 text/javascript 나 application/javascript 가
아니므로 자바스크립트 실행을 차단한다.
CSS에서도 JavaScript를 실행할 수 있으므로 사용자가 업로드한 파일이 .css 확장자가 아니면
CSS로 적용하는 것도 차단한다 - 따라서 첨부파일 업로드에서 업로드를 허용할 확장자 목록에
css 가 포함되면 안된다.
X-Content-Type-Options 헤더 시험
웹핵누리집의 파일 다운로드 기능에 X-Content-Type-Options: nosniff 헤더를
켜고 끌 수 있도록 하였다.
GET 변수 nosniff=off 이면
X-Content-Type-Options: nosniff 헤더를 전송하지 않는다.
시험에 사용할 예제 파일인 20161120-fake-jpg-real-js.jpg 파일은 다음과 같은 내용을 담고 있다.
확장자만 jpg 이고 실제 내용은 자바스크립트이다.
document.write('X-Content-Type-Options: nosniff 헤더가 선언되지 않으면 첨부파일을 자바스크립트로 실행할 수 있습니다.');
curl 명령어를 사용하여
X-Content-Type-Options: nosniff 헤더를 활성화 시켰을 때와 비활성화 시켰을 때의
통신 내용을 살펴보자.
root@kali:~# curl -v "http://webhack.dynu.net/?down=20161120-fake-jpg-real-js.jpg&nosniff=on"
* Trying 122.32.19.138...
* Connected to webhack.dynu.net (122.32.19.138) port 80 (#0)
> GET /?down=20161120-fake-jpg-real-js.jpg&nosniff=on HTTP/1.1
> Host: webhack.dynu.net
> User-Agent: curl/7.49.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 20 Nov 2016 15:14:48 GMT
< Server: Apache
< Set-Cookie: NormalCookie=Not-HttpOnly-Cookie
< Set-Cookie: HttpOnlyCookie=HttpOnly-Cookie; httponly
< X-Frame-Options: DENY
< X-XSS-Protection: 1
< X-Content-Type-Options: nosniff
< Content-Description: File Transfer
< Content-Disposition: attachment; filename="20161120-fake-jpg-real-js.jpg"
< Expires: 0
< Cache-Control: must-revalidate
< Pragma: public
< Content-Length: 150
< Content-Type: application/octet-stream
<
document.write('X-Content-Type-Options: nosniff 헤더가 선언되지 않으면 첨부파일을 자바스크립트로 실행할 수 있습니다.');
* Connection #0 to host webhack.dynu.net left intact
root@kali:~#
위는 X-Content-Type-Options: nosniff 헤더를 활성화시켰을 때의 통신내용이다.
파일 다운로드에서 URL 매개변수에 nosniff=on 를 추가하면 - 실제로는 일상시에 -
아파치 웹서버가 X-Content-Type-Options: nosniff 헤더를 전송한다.
root@kali:~# curl -v "http://webhack.dynu.net/?down=20161120-fake-jpg-real-js.jpg&nosniff=off"
* Trying 122.32.19.138...
* Connected to webhack.dynu.net (122.32.19.138) port 80 (#0)
> GET /?down=20161120-fake-jpg-real-js.jpg&nosniff=off HTTP/1.1
> Host: webhack.dynu.net
> User-Agent: curl/7.49.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 20 Nov 2016 15:13:35 GMT
< Server: Apache
< Set-Cookie: NormalCookie=Not-HttpOnly-Cookie
< Set-Cookie: HttpOnlyCookie=HttpOnly-Cookie; httponly
< X-Frame-Options: DENY
< X-XSS-Protection: 1
< Content-Description: File Transfer
< Content-Disposition: attachment; filename="20161120-fake-jpg-real-js.jpg"
< Expires: 0
< Cache-Control: must-revalidate
< Pragma: public
< Content-Length: 150
< Content-Type: application/octet-stream
<
document.write('X-Content-Type-Options: nosniff 헤더가 선언되지 않으면 첨부파일을 자바스크립트로 실행할 수 있습니다.');
* Connection #0 to host webhack.dynu.net left intact
root@kali:~#
위는 X-Content-Type-Options: nosniff 헤더를 비활성화시켰을 때의 통신내용이다.
파일 다운로드에서 URL 매개변수에 nosniff=off 를 추가하면
웹서버가 X-Content-Type-Options: nosniff 헤더를 전송하지 않는다.
가짜 JPG 파일의 JavaScript 실행여부 검사
가짜로 만든 JPG 파일과 X-Content-Type-Options: nosniff 헤더 선언 여부에 따라
웹브라우저가 자바스크립트를 어떻게 처리하는 지 살펴보자.
현재 당신이 접속한 웹브라우저는 "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com) "이다.
[X-Content-Type-Options 헤더가 없을 때]
- HTML:
<script type="text/javascript" src="/?down=20161120-fake-jpg-real-js.jpg&nosniff=off"></script>
- 자바스크립트 실행 결과:
[X-Content-Type-Options 헤더가 있을 때]
- HTML:
<script type="text/javascript" src="/?down=20161120-fake-jpg-real-js.jpg&nosniff=on"></script>
- 자바스크립트 실행 결과:
X-Content-Type-Options 헤더를 지원하는 웹 브라우저의 경우에는 자바스크립트 실행결과가 달라야 한다.
미설정시에는 자바스크립트가 "X-Content-Type-Options: nosniff 헤더가 선언되지 않으면 첨부파일을 자바스크립트로 실행할 수 있습니다. "라는 문자열을 출력한다. 설정 시에는 이 문자열이 출력되지 않는다.
X-Content-Type-Options 헤더를 지원하지 않는 웹 브라우저의 경우에는 자바스크립트 실행결과가 동일하게
"X-Content-Type-Options: nosniff 헤더가 선언되지 않으면 첨부파일을 자바스크립트로 실행할 수 있습니다. "라는 문자열을 출력한다.
이미지 파일에 대한 X-Content-Type-Options 헤더가 미치는 영향
X-Content-Type-Options 헤더는 크로스사이트스크립트 실행을 방지하기 위한 목적으로 제안되었다.
자바스크립트를 실행할 수 있는 text/javascript , text/css 등의 MIME 형식에 대해
사용될 것으로 예상할 수 있다.
실제 이미지 파일을 application/octet-stream MIME 형식으로 보냈을 때 이미지가 표시되는 지
살펴보자.
[ ↑ <img src="/?down=20161120-real-jpg.jpg&nosniff=off"> ]
[ ↑ <img src="/?down=20161120-real-jpg.jpg&nosniff=on"> ]
아마도 어떤 웹 브라우저를 쓰더라도 두 경우에 대해서 모두 "광화문과 해태상" 이미지가 출력될 것이다.
이는 X-Content-Type-Options 헤더가 설정되어 있더라도
"application/octet-stream " 마임형식을 "image/* " 마임형식으로 전용해서
쓰는 것을 허용한다는 뜻이다.
좀더 정확하게는 - 아마도 - 크롬이나 파이어폭스 등의 웹 브라우저는 실제 사용하고자 하는 마임형식이
"*/javascript ", "*/css "인 경우에만 X-Content-Type-Options 헤더를
검사할 겻으로 추정해볼 수 있다.
위에서 출력한 이미지의 HTTP 헤더를 보고자 할 경우에는 다음 명령어를 사용하면 확인할 수 있다.
curl -I "http://webhack.dynu.net/?down=20161120-real-jpg.jpg&nosniff=off"
curl -I "http://webhack.dynu.net/?down=20161120-real-jpg.jpg&nosniff=on"
X-Content-Type-Options 헤더 설정방법
[Apache 웹서버]
Apache 설정 파일에 다음 한줄을 추가하고 재기동한다.
Header set X-Content-Type-Options nosniff
[NginX 웹서버]
NginX 설정 파일에 다음 한줄을 추가하고 재기동한다.
add_header X-Content-Type-Options nosniff;
[Apache Tomcat WAS]
public class SampleResponseFilter implements Filter {
@Override
public void destroy() { }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// Protection against Type 1 Reflected XSS attacks
res.addHeader("X-XSS-Protection", "1; mode=block");
// Disabling browsers to perform risky mime sniffing
res.addHeader("X-Content-Type-Options", "nosniff");
chain.doFilter(req,res);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
}
[Apache Tomcat 8 WAS]
Tomcat 8에서는 web.xml 파일에 다음과 같이 추가한다.
<filter>
<filter-name>HeaderSecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HeaderSecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
[처음 작성한 날: 2016.11.20]
[마지막으로 고친 날: 2016.11.21]
< 이전 글 : webhack.dynu.net 문자배너 만들기 - toilet (2016.11.21)
> 다음 글 : X-XSS-Protection헤더 시험 페이지 (2016.11.19)
이 저작물은 크리에이티브
커먼즈 저작자표시 4.0 국제 라이선스에 따라 이용할 수 있습니다.
잘못된 내용, 오탈자 및 기타 문의사항은 j1n5uk{at}daum.net으로 연락주시기 바랍니다.
|