HTTP 응답 헤더 보안

의외로 웹 프로그래밍을 하는 사람들 조차도 HTTP 응답 헤더에 대해서 생각을 하지 않는다. HTTP 응답 헤더에 대한 생각을 하지 않는다는 것은 웹 프로그래밍을 깊게 공부한 사람이 아니라고 말할 수도 있다.

Cache Control

대부분의 사람들은 이 부분에 대해서는 잘 안다. Cache Control 헤더는 Client 에 컨텐츠를 어떻게 캐시할 것인지에 대한 제어를 할 수 있다.

하지만 고려해야할 사항이 존재한다. 만약 AWS Cloud 를 사용한다면 더 더욱 고려해야할 사항이 존재한다. AWS 의 CloudFront 는 CDN 서비스로서 컨텐츠 캐시를 기반으로 한다.

만일 특정 컨텐츠에 대해서 캐시를 하고 싶지 않다면 어떻게 해야할까? AWS 에서는 다음과 문서에 기술해 놨다.

사용자 지정 오리진 웹 서버 애플리케이션에서, Cache-Control no-cacheno-store 또는 private 명령을 CloudFront에서 캐시하지 않으려는 객체에 추가합니다. 또는 Expires 명령을 CloudFront에서 캐시하지 않으려는 객체에 추가합니다.

CloudFront에서 특정 파일 캐시를 방지하려면 어떻게 해야 합니까?

오리진(origin) 의 웹 서버나 애플리케이션에서 Cache-Control 헤더를 조작하도록 권고 하고 있다.

위는 응답 캐싱 정책을 정의하는 사용하는 헤더 지시문이다. Cache-Control 은 HTTP/1.1 사양의 일부로 정의 되었다. Expire 는 Cache-Control 이전에 사용했던 지시문이다. 현대의 대부분 브라우저는 HTTP/1.1 를 지원하지만 구형을 쓰는 곳이 있을 수 있음으로 Expire 지시문을 함께 사용한다.

no-cache 는 말그대로 캐시를 하지 말라는 것을 지시한다. 이 지시자를 쓸 경우에 매번 요청할 때마다 문서의 유효성을 검사한다. 그래서 컨텐츠가 변경된 경우에 최신 버전을 가지고 오게 된다. 컨텐츠의 변화를 감지하는 매커니즘을 가진다는 것이 매우 중요하다. 또 모든 것을 캐시를 하지 않는다. 예를들어, 비공계 개인 데이터와같은 것은 이 지시자로 캐시를 제어를 할 수 없다.

no-store 는 비공개 개인 데이터를 포함해 모든 캐시를 하지 않다록 해준다.

public, private 이라는 것도 있다. 이는 중간 캐시를 이용할 경우에 유용하다. 중간 캐시는 CDN 을 의미한다. 만일 CDN 과 같은 중간 캐시 서버가 캐시 데이터를 가지고 있기를 원하지 않는다면 private 를 써야 한다.

그리고 위 코드에 보이는과 같이 모든 것을 다 포함하도록 지시문을 사용해서는 안된다. Pragma, Expire 는 HTTP/1.0 스펙에서 유효하다. 거기다 Cache-Control 지시문에서 no-cache, no-store, max-age 를 한꺼번에 쓰는건 잘못된 것이다.

그래서 cache 전략이 필요하게된다. 이는 HTML 구조를 봐야 한다.

index.html 파일 안에 구조가 저렇게 되어 있다고 가정하면, 대략 다음과 같은 형태의 캐시 전략을 쓸 수 있다.

ContentsCache-Control
index.htmlCache-Control: no-cache
common.cssCache-Control: max-age=86400
a.jsCache-Control: private, max-age=86400

CSS 파일은 최대 1일까지만 캐시하도록 지시했고, Javascript 파일은 CDN 에서 캐시를 하지 못하도록 하고 브라우져에서 최대 1일까지만 캐시하도록 했다. index.html 은 no-cache 를 주어서 캐시를 하지 못하게 했지만 매번 요청할 때마다 변경이 되었는지 체크하고 변경되었다면 최신판을 새로 받도록 했다.

must-revalidate 가 있는데, 강제 사항을 적용할 때 사용 한다. 이는 다음과 같이 사용한다.

이 경우에 1일동안 캐시를 하고 그 이후에는 서버에 반드시 유효성검사를 하도록 강제한다. 만일 네트워크 상황으로 유효성 검사를 못할 경우에는 절대로 캐시 데이터를 사용하지 못하도록 한다.

이론을 기반으로 현실에서 사용 전략을 다음과 같이 세울 수 있다.

경우의 수Cache-Control
금융Cache-Control: no-store
장바구니, 결재 페이지Cache-Control: no-store
개인정보 수정 페이지Cache-Control: no-store
동적 웹 페이지Cache-Control: no-cache
정적 파일Cache-Control: max-age=86400

금융 관련된 웹 애플리케이션을 사용할 때에는 캐시를 되도록이면 하게 하면 안된다.

Nginx 와 같은 경우에 다음과 같이 HTTP Header 에 Cache-Control 을 붙일 수 있다.

Nginx 에서 max-age 설정은 expires 해도 된다. 이렇게 하면 구형의 Expire 지시자도 함께 지정된다.

Spring에서는 Spring-Security 에서 다음과 같이 지원한다.

위 설정은 다음과 같은 상태로 만든다.

만일 직접 캐시를 조작하고 싶다면 다음과 같이 해주면 된다.

Content Type Options

브라우저는 content sniffing 같은 기술을 사용해 다운받을려는 컨텐츠 타입을 추정하고 특정하게 된다. 하지만 이러한 것은 XSS 와 같은 공격을 제공하는 빌미가 된다. 그래서 이것을 방지하기 위해서 다음과 같이 헤더응답을 넣는다.

Spring Security 에서는 간단하게 설정할 수 있다.

HTTP Strict Transport Security(HSTS)

보통 사이트에 접속하는데에 프로토콜을(HTTPS, HTTP) 붙이지 않는다. 이럴 경우 브라우저는 HTTP 로 먼저 접속해보고 서버의 설정에 따라서 HTTPS 로 리다이렉트를 하게 된다.

만일 중간에 해커가 Proxy Server 를 두게되면 클라이언트는 HTTP 로만, Proxy Server 가 이를 받아서 HTTPS 로 뒷단 서버에 연결을 하게된다. 이렇게 되면 모든 정보를 볼 수 있게 된다. 이러한 해킹 기법을 “SSL Stripping” 공격 혹은 SSL/TLS Hijacking 이라고 부른다.

HSTS 는 애초에 접속을 HTTPS 로만 하라고 강제한다. 이는 브라우저와 서버 모두 HSTS 를 모두 지원해야 한다는 제한이 있지만 2010에 HSTS 에 대한 논의로 인해서 현재에는 모든 서버와 브라우저가 지원하고 있다.

HSTS 를 지원하는 브라우저는 도메인 목록을 유지한다. HTTPS 로만 접속 해야만 하는 도메인을 가지고 있게된다. 만일 HSTS 목록에 있는 도메인 주소를 억지로 HTTP 로 접속하게 되면 서버가 리다이렉트를 하는게 아니라 HTTPS 로 다시 접속하라고 브라우저에 요청하게 된다. HSTS 는 목록에 도메인을 유지하는 시간을 지정할 수 있다.

무조건 HTTPS 로만 접속하도록 강제하기 때문에 SSL 도메인 인증서가 있는 사이트에서만 사용 가능하다.

Spring Security 에서는 다음과 같이 간단히 적용할 수 있다.

X-Frame-Options

HTML 내에 또 다른 frame 을 넣는 기술은 자주 사용 되었었다. 최근에는 사용이 줄었지만 그래도 여전히 사용되어지는 기술이다. 하지만 이러한 frame 을 이용할 경우에 그것이 변조에 취약하다는 문제가 있다.

그래서 이 frame 을 실행하지 못하도록 브라우저에게 요청할 수 있는데, 이것이 X-Frame-Options 다. 기본 값은 DENY 이지만 같은 도메인의 frame 일 경우에는 허용할 수도 있다.

Spring Security 에서는 다음과 같이 설정할 수 있다.

SAMEORIGIN 일 경우에는 같은 도메인의 frame 일 경우에는 허용하게 한다.

X-XSS-Protection

XSS 를 브라우저에서 실행하도록 한다. 주의해야할 것은 서버의 웹 애플리케이션에서 하는게 아니라 브라우저의 XSS 필터에서 하는 것이다. 그러니까 서버에 요청을 넣기전에 브라우저가 사전에 XSS를 한번 차단하게 하는 것이다.

Spring Security 에서는 다음과 같이 설정할 수 있다.

CONTENT-SECURITY-POLICY

웹에 자원은 동일한 도메인에서만 작동되도록 설계되었다. 하지만 자신만의 스크립트를 삽입하는 XSS 공격을 고안해 냈다. 이를 방어하기 위해서 고안된 것이 CSP(Content Security Policy) 이다. 이것을 HTTP 헤더에 정의하면 브라우저에는 이곳에서 받은 리소스만 실행허거나 렌더링하도록 강제 한다.

Spring Seuciryt 에서는 다음과 같이 설정할 수 있다.

Spring 에서 기본

많은 웹 애플리케이션이 Spring 을 이용해서 만들어진다. 그리고 인증을 위해서 Spring Security 를 사용한다. 하지만 대부분의 소스를 보면 위에서 언급한 기본적인 보안조차 되어 있지 안되어 있는 곳이 대부분이다. 대기업의 프로젝트에서조차도 이런것을 고려하지도 않는다.

위에서 언급한 모든 내용은 다음과 같이 Spring Security 에서 간단하게 설정할 수 있다.

아주 간단하다. 그냥 가져다 붙여넣기만 해도 된다. 하지만 이것조차도 사용하지 않는 대기업 프로젝트가 부지기수다. 뭐가 기술력이 좋다는 건지… IT 선진국이라는 것이 좋은 인프라에 작동하기만 하면 되는 앱만으로 얻을 수 있는 거였다니..

Post a comment

You may use the following HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">