본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
HTTP Strict Transport Security, HSTS; HTTP 엄격 전송 보안
서버가 "앞으로 정해진 기간 동안 이 도메인에는 HTTPS로만 접속하라"고 브라우저에 알려 두는 정책

HTTP Strict Transport Security(HSTS)는 웹 서버가 Strict-Transport-Security 응답 헤더로 자기 도메인을 "HTTPS 전용"이라고 선언하면, 브라우저가 그 기간 동안 해당 도메인으로 가는 모든 요청을 스스로 HTTPS로 바꾸고 인증서 오류가 나면 접속을 끊게 하는 정책 메커니즘이다. IETF 표준 트랙 문서 RFC 6797(2012년 11월)로 정의되어 있다.[1]

HTTP에서 HTTPS로 넘기는 301 리다이렉트만으로는 첫 평문 요청이 네트워크에 노출된다. SSL 스트리핑 같은 중간자 공격은 이 틈을 노린다. HSTS는 브라우저가 평문 요청을 아예 보내지 않게 해서 이 공격을 막고, 쿠키 탈취(세션 하이재킹)도 어렵게 한다.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
지시어 정의 뜻
max-age=초 RFC 6797, 필수 이 호스트를 HSTS 호스트로 기억할 기간. 31536000초는 365일. 헤더를 받을 때마다 기간이 새로 시작된다
includeSubDomains RFC 6797, 선택 모든 하위 도메인에도 같은 정책 적용
preload RFC에 없음, hstspreload.org 관례 브라우저 내장 프리로드 목록에 넣어도 된다는 도메인 소유자의 동의 표시
  • 헤더는 HTTPS 응답에 실려 왔을 때만 효력이 있다. 평문 HTTP 응답의 Strict-Transport-Security는 무시해야 한다(RFC 6797 8.1절). 평문 헤더를 인정하면 공격자가 가짜 헤더로 사이트를 접속 불가 상태로 만들 수 있기 때문이다.[1]
  • max-age=0을 보내면 브라우저가 그 호스트를 HSTS 목록에서 지운다. 정책을 끄는 공식 방법이다.
  • IP 주소로 접속한 경우는 HSTS 호스트로 기록하지 않는다.
  • 지시어 이름은 대소문자를 구분하지 않고, 같은 지시어가 두 번 나오면 헤더 전체가 무효다.

서버 설정 예

편집 원본 편집
# nginx: HTTPS server 블록 안
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Apache httpd (mod_headers)
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

Cloudflare 같은 CDN은 관리 화면의 옵션으로 헤더를 붙여 준다. 개발 프레임워크에도 설정이 있다(Spring Security의 headers().httpStrictTransportSecurity(), Express의 helmet 등).

브라우저가 HSTS 정책을 가진 도메인(Known HSTS Host)에 대해 하는 일은 두 가지다.[1]

  1. 주소창 입력, 링크, 스크립트 요청 어디서든 http:// URL을 네트워크에 보내기 전에 https://로 바꾼다(포트 80은 443으로). Chrome 개발자 도구에서는 이것이 "307 Internal Redirect"로 보이는데, 서버가 보낸 응답이 아니라 브라우저 안에서 일어난 변환이다.
  2. TLS 연결에 문제가 있으면(인증서 만료, 신뢰할 수 없는 발급자, 이름 불일치 등) 경고 화면에서 "무시하고 계속" 선택지를 주지 않고 접속을 끊는다. RFC는 이를 "no user recourse"라고 표현한다(8.4절, 12.1절).

SSL 스트리핑 방어

편집 원본 편집

SSL 스트리핑은 Moxie Marlinspike가 2009년 블랙햇에서 공개한 공격이다. 공격자가 사용자와 서버 사이에서 평문 HTTP 구간을 가로채, 서버의 HTTPS 리다이렉트나 페이지 속 https:// 링크를 http://로 바꿔 전달하고 자신은 서버와 HTTPS로 통신한다. 사용자는 주소창의 자물쇠가 없다는 것 말고는 이상을 알기 어렵다. 당시에는 HTTPS를 안 쓰는 사이트가 많아서 "원래 평문인 사이트"와 "공격으로 평문이 된 사이트"를 브라우저가 구별할 수 없었다.

HSTS는 이 구별 정보를 브라우저에 저장해 둔다. 한 번 HSTS 헤더를 받은 도메인이라면 사용자가 http://bank.example을 입력해도 평문 요청이 나가지 않으므로 공격자가 끼어들 평문 구간이 없다. 공격자가 자기 인증서로 HTTPS를 흉내 내도 인증서 오류를 무시할 수 없으니 통하지 않는다.

첫 방문 문제(TOFU)와 프리로드 목록

편집 원본 편집

헤더 방식 HSTS는 최소 한 번은 HTTPS로 정상 접속해 헤더를 받아야 작동한다. 이런 방식을 첫 사용 시 신뢰(Trust On First Use, TOFU)라고 한다. 처음 방문, 브라우저 데이터 삭제 직후, max-age가 지난 뒤의 첫 요청은 보호받지 못한다(RFC 6797 14.6절).

이를 메우는 것이 HSTS 프리로드 목록이다. Chrome 팀이 관리하는 도메인 목록을 브라우저 바이너리에 넣어 배포하고, Firefox, Safari, Edge, Opera도 이 목록을 바탕으로 한 목록을 쓴다. 목록에 있는 도메인은 첫 요청부터 HTTPS로만 접속된다. 등록은 무료이고 hstspreload.org에서 신청한다. 요건은 다음과 같다.[2]

  1. 유효한 인증서를 제공한다.
  2. 포트 80에서 서비스한다면 같은 호스트의 HTTPS로 리다이렉트한다.
  3. 모든 하위 도메인을 HTTPS로 제공한다(특히 www가 DNS에 있다면 그것도).
  4. 기본 도메인(apex)의 HTTPS 응답에 HSTS 헤더를 보내며, max-age는 31536000(1년) 이상, includeSubDomains와 preload를 포함한다.

hstspreload.org는 곧바로 1년짜리 헤더를 걸지 말고 max-age=300(5분), 604800(1주), 2592000(1개월) 순으로 올리면서 문제가 없는지 확인한 뒤 신청하라고 권한다.

TLD 전체 프리로드

편집 원본 편집

프리로드 목록에는 최상위 도메인(TLD) 전체를 넣을 수도 있다. Google은 2015년 .google을 목록에 넣어 첫 "보안 TLD"를 만들었고, 2017년 9월 .foo, .dev를 시작으로 자사 TLD들에 HSTS를 확대한다고 발표했다.[3] 이후 일반 등록을 연 .app, .dev, .page 등은 도메인을 사면 곧바로 HTTPS 전용이 된다. 등록자가 따로 신청하지 않아도 되고, 목록 추가 후 브라우저 업데이트가 퍼지기를 기다릴 필요도 없다.

.dev가 HSTS 목록에 들어가자, 로컬 개발용으로 myapp.dev 같은 가짜 도메인을 hosts 파일에 적어 쓰던 개발자들의 환경이 HTTPS를 강제받아 접속되지 않는 일이 생겼다. 로컬 개발에는 예약된 .test, .localhost를 쓰는 것이 맞다.

문제 설명
첫 방문 프리로드 목록에 없는 도메인은 첫 요청이 보호받지 못한다
되돌리기 어려움 헤더로 1년을 선언하면 이미 받은 브라우저는 서버가 HTTPS를 그만둬도 1년 동안 HTTP로 오지 않는다. 프리로드 목록에서 빼는 것도 브라우저 업데이트 주기를 따르므로 수개월이 걸리고, 옛 버전 브라우저에는 계속 남는다[2]
인증서 사고 = 전면 장애 인증서가 만료되거나 잘못 설정되면 사용자가 경고를 무시하고 들어올 방법이 없다
하위 도메인 누락 includeSubDomains를 걸면 HTTPS가 없는 사내 도구, 장비 관리 화면, 오래된 하위 도메인이 모두 접속 불가가 된다. 프리로드 전에 전수 점검이 필요하다
비슷한 가짜 도메인 공격자가 examp1e.com처럼 비슷한 다른 도메인으로 유도하면 HSTS는 도움이 안 된다. DNS 스푸핑으로 목록에 없는 이름을 쓰게 하는 경우도 같다
시간 조작 정책이 기간 기반이라, 가짜 NTP 응답으로 피해자 컴퓨터 시계를 먼 미래로 돌리면 정책을 만료시킬 수 있다. RFC는 이를 명세 범위 밖이라고 적는다(14.7절)
TLS 자체 공격 BEAST, CRIME 같은 TLS 계층 공격이나 서버 침해는 HSTS와 무관하다

추적(슈퍼 쿠키) 문제

편집 원본 편집

HSTS 상태는 사용자가 쿠키를 지워도 남는 경우가 많았다. 공격자가 하위 도메인 여러 개(예: 20개)를 준비해 방문자마다 서로 다른 조합으로 HSTS를 켜 두고, 다음 방문 때 어느 하위 도메인 요청이 HTTPS로 올라가는지 관찰하면 2^20(약 100만) 가지 식별자를 만들 수 있다. RFC 6797도 12.5절에서 이 가능성을 인정한다.[1] 이런 방식을 흔히 "HSTS 슈퍼 쿠키"라고 부른다.

브라우저들은 대응책을 내놓았다. WebKit(Safari)은 2018년 HSTS 상태를 현재 로드된 호스트 이름이나 등록 가능한 최상위 도메인(TLD+1)에 대해서만 설정하게 하고, 서드파티 쿠키가 차단된 하위 자원 요청에서는 HSTS 상태를 무시하게 바꾸었다.[4] Firefox는 2021년 1월 26일 출시한 Firefox 85부터 HSTS 캐시를 포함한 여러 캐시를 최상위 사이트별로 분리(partitioning)해 교차 사이트 추적에 쓰지 못하게 했다.[5]

관련 기술과의 관계

편집 원본 편집

HTTPS DNS 레코드

편집 원본 편집

RFC 9460(2023년 11월)이 정의한 SVCB/HTTPS DNS 자원 레코드는 도메인이 HTTPS를 지원한다는 사실, 지원 프로토콜(ALPN, 예: HTTP/3), 접속 힌트를 DNS로 알린다. HTTPS 레코드가 있는 도메인의 http:// URL에 대해 클라이언트는 307 리다이렉트를 받은 것처럼 HTTPS로 올려 접속해야 하고(SHOULD), 인증서 오류 시 사용자가 무시하고 진행하는 선택지를 없앨 수도 있다(MAY). 그래서 HTTPS 레코드를 게시하는 사이트는 사용자의 경고 무시에 기대면 안 된다.[6]

즉 HTTPS 레코드는 첫 방문 문제를 DNS로 풀어 주는 HSTS 비슷한 신호다. 다만 RFC는 이 신호를 평문 HTTP로 받은 307 리다이렉트 이상으로 믿지 말라고 한다. DNS 응답은 DNSSEC이나 암호화 DNS가 없으면 위조될 수 있기 때문이다. 기간을 기억하는 HSTS와 달리 레코드가 사라지면 효력도 사라진다. 두 방식은 서로 보완하는 관계다.

HTTP Public Key Pinning(HPKP, RFC 7469)은 특정 공개 키가 들어간 인증서만 받아들이게 하는 헤더였다. HSTS와 같은 "브라우저에 정책을 기억시키는" 방식이라 같은 위험(잘못 설정하면 장기간 접속 불가, 추적 악용)을 더 크게 안고 있었고, 운영 사고와 악용 우려 때문에 주요 브라우저에서 지원이 제거되었다. 현재는 인증서 투명성(Certificate Transparency)이 그 역할을 대신한다.

브라우저의 HTTPS 우선 모드

편집 원본 편집

최근 브라우저는 HSTS가 없는 사이트도 먼저 HTTPS로 시도하는 모드(Chrome의 HTTPS-First, Firefox의 HTTPS 전용 모드 등)를 두고 있다. 이것은 브라우저 쪽 설정이고, 인증서 오류 시 경고 무시를 막는 HSTS의 효과까지 주지는 않는다.

  • 2008년: Collin Jackson과 Adam Barth가 논문 "ForceHTTPS: Protecting High-Security Web Sites from Network Attacks"에서 사이트가 브라우저에 HTTPS 강제를 요청하는 방식을 제안했다. HSTS의 원형이다.[7]
  • 2009년 9월 18일: PayPal의 Jeff Hodges와 Jackson, Barth가 "Strict Transport Security(STS)" 초안을 공개했다.
  • 2010년: 크로미엄(Chrome)이 HSTS를 구현하면서 브라우저에 내장하는 프리로드 목록도 함께 도입했다. 다른 브라우저의 목록은 이 Chrome 목록을 바탕으로 만들어진다.[8] 같은 해 6월 IETF 인터넷 초안이 되면서 HTTP에만 적용된다는 뜻으로 이름에 "HTTP"가 붙었다. 헤더 이름은 Strict-Transport-Security로 남았다.
  • 2012년 11월: RFC 6797(표준 제안, Proposed Standard)로 발행되었다. 저자는 J. Hodges(PayPal), C. Jackson(카네기멜런 대학교), A. Barth(Google). 같은 달 출시된 Firefox 17이 Chrome 목록에서 가져와 검증한 프리로드 목록을 넣었다.[9]
  • 2013년 무렵: Safari가 OS X 매버릭스(10.9)와 함께 HSTS를 지원한 것으로 알려져 있다.
  • 2015년: Windows 10의 Edge와 IE 11이 HSTS를 지원했고, Windows 7·8.1의 IE 11도 6월 업데이트(KB3058515)로 지원했다. 같은 해 Google이 .google TLD를 프리로드했다.
  • 2017년 9월: Google이 자사 TLD 전체 프리로드 확대를 발표했다(.foo, .dev부터).[3]