Strict-Transport-Security
사용법
Strict-Transport-Security 헤더는 HTTP Strict Transport Security(HSTS)를 뒷받침하는 메커니즘입니다. 브라우저가 보안 연결을 통해 이 헤더를 받으면 해당 디렉티브를 기록하고, 이후 지정된 도메인에 대한 모든 HTTP 요청을 자동으로 HTTPS로 업그레이드합니다. 보안 연결을 사용할 수 없으면 브라우저는 평문 HTTP로 되돌아가는 대신 접근 자체를 거부합니다.
이 헤더는 HTTPS를 통해 전달될 때만 존중됩니다. 평문 HTTP로 제공된 응답에 담긴 Strict-Transport-Security는 무시되는데, 네트워크 경로상의 공격자가 헤더를 주입하거나 변조할 수 있기 때문입니다.
이 헤더는 max-age, includeSubDomains, preload라는 세 가지 디렉티브를 받습니다.
max-age
max-age 디렉티브는 필수입니다. 값은 브라우저가 해당 도메인에 HTTPS를 강제하도록 기억하는 초 수를 지정합니다. 운영 환경에서 흔히 쓰이는 값은 63072000(2년)입니다. max-age=0으로 설정하면 브라우저 캐시에서 HSTS 항목이 즉시 제거됩니다.
includeSubDomains
includeSubDomains 디렉티브는 HTTPS 강제를 해당 호스트의 모든 하위 도메인으로 확장합니다. 이 디렉티브가 없으면 HSTS는 응답에 담긴 정확한 도메인에만 적용됩니다. includeSubDomains를 추가하면 공격자가 평문 HTTP 하위 도메인을 악용해 쿠키를 설정하거나 트래픽을 리다이렉트하는 것을 막을 수 있습니다.
preload
preload 디렉티브는 HSTS 명세의 일부가 아닙니다. 브라우저 공급업체가 HSTS preload 목록을 관리하며, preload 디렉티브는 도메인 소유자가 목록에 포함되기를 원한다는 의사를 나타냅니다. 도메인이 preload 목록에 올라가면 브라우저는 첫 연결부터 HTTPS를 강제하므로, 첫 Strict-Transport-Security 헤더를 받기 전의 취약한 구간이 사라집니다. 도메인을 preload 목록에 제출하려면 최소 1년(31536000초)의 max-age와 includeSubDomains 디렉티브가 필요합니다.
예시
모든 하위 도메인에 대해 2년간 HTTPS를 강제하고 preload 자격을 갖춘 서버입니다.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
기본 도메인에만 6개월 동안 HSTS를 설정하는 최소 구성입니다.
Strict-Transport-Security: max-age=15768000
max-age를 0으로 재설정하여 HSTS 항목을 지웁니다. 마이그레이션이나 테스트 중에 유용합니다.
Strict-Transport-Security: max-age=0
문제 해결
브라우저가 정책을 로컬에 캐시하기 때문에, HSTS 문제는 서버를 바꾼 뒤에도 계속되는 연결 실패나 리다이렉트 루프로 드러나는 경향이 있습니다.
긴 max-age로 HSTS를 켠 뒤 접근이 막힙니다. 브라우저가 HSTS 정책을 기록하면 모든 요청이 자동으로 HTTPS로 업그레이드됩니다. max-age가 만료되거나 사용자가 수동으로 HSTS 항목을 지우기 전까지는 HTTP로 되돌릴 수 없습니다. Chrome에서는 chrome://net-internals/#hsts 를 열고 “Delete domain security policies”에 도메인을 입력해 항목을 삭제하십시오. Firefox에서는 브라우저를 닫고 프로필 폴더의 SiteSecurityServiceState.txt를 편집하여 해당 도메인 줄을 제거하십시오.
includeSubDomains로 인한 하위 도메인 장애. includeSubDomains를 추가하면 내부 도구, 스테이징 서버, 유효한 인증서가 없는 레거시 서비스를 포함한 모든 하위 도메인에 HTTPS가 강제됩니다. 이 디렉티브를 켜기 전에 모든 하위 도메인이 신뢰할 수 있는 TLS 인증서를 제공하는지 확인하십시오. 하위 도메인이 망가졌다면 includeSubDomains를 제거하고 max-age=0으로 헤더를 다시 배포하여 캐시된 항목을 지우십시오.
preload 목록에서 제거되기까지 몇 달이 걸립니다. HSTS preload 목록은 브라우저 릴리스와 함께 배포되므로, 제거하려면 hstspreload.org에 요청을 제출하고 다음 목록 갱신을 기다린 뒤, 그 갱신이 브라우저 버전 업그레이드를 통해 사용자에게 도달하기를 기다려야 합니다. 이 과정은 몇 달이 걸립니다. preload는 장기적인 약속으로 여기고 제출 전에 HTTPS 준비가 완전한지 확인하십시오.
평문 HTTP로 보낸 HSTS 헤더는 무시됩니다. 응답이 암호화되지 않은 연결로 도착하면 브라우저는 Strict-Transport-Security 헤더를 버립니다. 이 헤더는 HTTPS 응답에서만 효력을 가집니다. curl -sI https://example.re | grep -i strict 로 확인하여 HTTPS 응답에 헤더가 있는지 점검하십시오.
HSTS를 켠 뒤 발생하는 혼합 콘텐츠 오류. HSTS는 페이지를 HTTPS로 업그레이드하지만, 여전히 http:// URL을 참조하는 내장 리소스는 혼합 콘텐츠 경고나 차단을 유발합니다. 모든 리소스 URL(이미지, 스크립트, 스타일시트, API 엔드포인트)을 점검하여 https:// 또는 프로토콜 상대 경로를 쓰도록 참조를 갱신하십시오. 남아 있는 하드코딩된 HTTP URL은 CSP 디렉티브 upgrade-insecure-requests가 자동으로 처리해 줍니다.
인증서 갱신 실패로 사이트 전체가 잠깁니다. HSTS로 보호된 도메인에서 인증서가 만료되었거나 유효하지 않으면 사용자가 우회할 수 없는 완전한 실패로 이어집니다. 브라우저는 사이트 표시를 거부하며 “무시하고 계속” 옵션도 제공하지 않습니다. 인증서 갱신을 자동화하고(Let’s Encrypt certbot은 cron 작업으로 이를 처리합니다) 인증서 만료일을 모니터링하십시오. 만료 최소 14일 전에 알림이 울리도록 설정하십시오.