HTTPS란 무엇인가: TLS와 인증서로 HTTP를 보호하는 원리

HTTPS란 무엇인가: TLS와 인증서로 HTTP를 보호하는 원리

브라우저 주소창에 작은 자물쇠 아이콘이 켜져 있으면 우리는 별다른 의심 없이 정보를 입력합니다. 비밀번호를 치고, 카드 번호를 넣고, 메시지를 보내는데요. 이 자물쇠 한 칸이 사실은 HTTP 위에 한 겹의 보호막을 덧씌운 결과인 셈입니다.

HTTPS는 이름에서도 짐작되듯 HTTP에 보안(Secure)을 보탠 형태입니다. 정확히 말하면 HTTP를 TLS라는 암호 통신 위에서 흘려보내는 구조인데요. 이번 글에서는 평문 HTTP가 왜 위험한지, TLS가 그 위에 무엇을 더하는지, 그리고 우리가 평소에 보는 인증서와 자물쇠가 어떻게 신뢰를 만드는지를 차근차근 풀어보겠습니다.

평문이 위험한 이유

HTTP만으로 통신하던 시대에는, 같은 네트워크에 앉은 누구나 패킷을 들여다볼 수 있었습니다. 패킷에 담긴 글자는 사람이 읽을 수 있는 평문이라, 와이파이 같은 공유 매체에서는 옆자리 사람이 내가 보낸 비밀번호를 그대로 볼 수 있었던 셈인데요. 조금 더 적극적인 공격자라면 패킷 일부를 바꿔치기해서, 보낸 사람과 받은 사람이 영영 알아채지 못하는 상태로 메시지를 조작할 수도 있었습니다.

심지어 통신 상대가 진짜 그 서버인지조차 보장할 수 없었습니다. DNS 응답을 가로채거나 ARP 테이블을 흔들면, 같은 도메인 이름으로 들어와도 실제로는 공격자의 서버에 연결되도록 유도할 수 있었거든요. 엿보기, 바꿔치기, 가짜 서버라는 세 가지 문제를 한 번에 다루기 위해 등장한 것이 TLS이고, 그 위에 HTTP를 얹은 결과가 HTTPS입니다.

HTTP를 감싸는 TLS 한 겹

TLS는 Transport Layer Security의 줄임말입니다. 이름 그대로 TCP 같은 전송 계층 위에서 동작하면서, 그 위로 흐르는 데이터를 암호화해 주는 별도의 약속인데요. HTTPS는 새로운 프로토콜을 발명한 것이 아니라, “HTTP 메시지를 TLS로 한 번 감싸서 보내자”는 합의에 가깝습니다.

flowchart TB
    accTitle: HTTPS의 프로토콜 계층
    accDescr: HTTP 1.1과 HTTP 2 메시지는 TLS로 보호되어 TCP와 IP를 거치고, HTTP 3 메시지는 TLS 1.3을 통합한 QUIC으로 보호되어 UDP와 IP를 거쳐 전송된다.

    http["HTTP 요청과 응답"] --> version{"HTTP 버전"}
    version -- "HTTP/1.1 또는 HTTP/2" --> tls["TLS"]
    tls --> tcp["TCP"]
    version -- "HTTP/3" --> quic["QUIC<br/>TLS 1.3 통합"]
    quic --> udp["UDP"]
    tcp --> network["IP"]
    udp --> network

    classDef application stroke:#7c3aed,stroke-width:3px
    classDef decision stroke:#0d9488,stroke-width:3px
    classDef security stroke:#0d9488,stroke-width:3px
    classDef infrastructure stroke:#0284c7,stroke-width:2px

    class http application
    class version decision
    class tls,quic security
    class tcp,udp,network infrastructure

HTTP/1.1과 HTTP/2에서는 TLS가 TCP 위에서 별도의 보안 계층을 이루고, HTTP/3에서는 TLS 1.3이 QUIC에 통합됩니다. 구현 위치는 다르지만 HTTP 데이터를 보호한 뒤 네트워크로 보낸다는 역할은 같습니다.

이런 보호 계층이 들어가도 응용 코드에서는 거의 변화가 없습니다. 같은 GET 요청을 같은 헤더로 보내면 되고, 브라우저의 보안 계층은 그 데이터를 암호화한 뒤 TCP나 UDP를 사용하는 전송 경로로 흘려보내는 일을 알아서 처리하죠. 서버 쪽에서도 보안 계층이 복호화한 다음 HTTP 메시지로 풀어주기 때문에, 양쪽 응용 코드는 평문 시절과 거의 똑같은 인터페이스를 그대로 씁니다.

TLS 핸드셰이크가 하는 일

암호화된 통신을 시작하려면, 먼저 둘이 같은 비밀 키를 갖고 있어야 합니다. 그런데 이 키를 어떻게 안전하게 나눠 가질 수 있을까요? 공격자가 통신을 모두 보고 있는 상황에서 키를 그대로 보낼 수는 없습니다. 이 문제를 푸는 절차가 바로 TLS 핸드셰이크입니다.

flowchart TB
    accTitle: HTTPS 연결이 준비되는 과정
    accDescr: 브라우저와 서버가 TLS 버전과 암호 도구를 고르고 연결마다 사용할 키를 만든 뒤, 인증서와 서명으로 서버를 확인하면 보호된 채널에서 HTTP 요청과 응답을 주고받는다.

    connect(["브라우저가 HTTPS 접속"])
    negotiate["TLS 버전과<br/>암호 도구 협상"]
    derive["연결별 대칭키 생성"]
    authenticate["인증서와 서명으로<br/>서버 확인"]
    channel(["보호된 채널 완성"])
    http["HTTP 요청과 응답"]

    connect --> negotiate --> derive --> authenticate --> channel --> http

    classDef endpoint stroke:#7c3aed,stroke-width:3px
    classDef handshake stroke:#0284c7,stroke-width:2px
    classDef secure stroke:#16a34a,stroke-width:3px

    class connect endpoint
    class negotiate,derive,authenticate handshake
    class channel,http secure

핵심은 HTTP를 보내기 전에 보호된 통신 채널부터 만든다는 점입니다. 브라우저와 서버는 연결마다 사용할 키를 안전하게 만들고, 인증서와 서명으로 서버의 신원을 확인한 뒤에야 HTTP 요청과 응답을 주고받습니다.

여기서는 HTTPS가 얻는 결과에만 집중하겠습니다. ClientHelloServerHello, ECDHE 키 합의, 순방향 비밀성처럼 핸드셰이크 내부에서 벌어지는 일은 TLS 핸드셰이크의 암호학적 원리에서 단계별로 살펴볼 수 있습니다.

인증서가 만드는 신뢰의 사슬

핸드셰이크에서 또 한 가지 중요한 일이 일어나는데, 바로 서버가 자신이 누구인지를 증명하는 단계입니다. 서버는 ServerHello 뒤의 암호화된 핸드셰이크 구간에서 자신의 디지털 인증서를 보내고, 클라이언트는 이 인증서를 검증해 “이 서버가 정말 example.com의 주인인가”를 확인하죠.

인증서는 공개 키와 도메인 이름, 유효 기간 같은 정보를 담은 문서입니다. TLS 인증서의 X.509 구조를 알면 각 필드와 파일이 맡는 역할을 더 정확히 이해할 수 있습니다. 그리고 그 문서 끝에는 인증 기관(CA, Certificate Authority)의 서명이 붙어 있는데요. 브라우저나 운영 체제는 미리 신뢰할 만한 CA들의 공개 키를 자기 안에 들고 있어서, 그 서명을 검증하면 인증서를 믿을 수 있는지 판단할 수 있습니다.

여기서 흥미로운 부분은 인증서가 한 단계로 검증되지 않는다는 점입니다. 실제로는 보통 “내 사이트 인증서 → 중간 인증서 → 루트 인증서” 식으로 사슬이 이어지는데요. 브라우저는 사슬의 끝(루트)을 자기가 알고 있는지를 확인해서, 만약 안다면 그 사슬 전체를 신뢰합니다. 이 구조를 신뢰의 사슬(chain of trust)이라고 부르고, HTTPS의 신원 보장이 작동하는 토대입니다.

flowchart TB
    accTitle: 인증서 신뢰의 사슬을 검증하는 흐름
    accDescr: 브라우저는 사이트 인증서의 발급자 서명을 중간 인증서로 검증하고, 중간 인증서의 발급자 서명을 루트 인증서로 검증한 뒤 그 루트가 운영 체제나 브라우저의 신뢰 저장소에 있는지 확인한다.

    site["사이트 인증서<br/>도메인과 공개 키"]
    intermediate["중간 인증서<br/>중간 인증 기관"]
    root["루트 인증서<br/>루트 인증 기관"]
    trustStore(["브라우저와 운영 체제의<br/>신뢰 저장소"])

    site -->|"발급자 서명 검증"| intermediate
    intermediate -->|"발급자 서명 검증"| root
    root -->|"미리 등록된 루트인지 확인"| trustStore

    classDef certificate stroke:#0284c7,stroke-width:2px
    classDef trust stroke:#16a34a,stroke-width:3px

    class site,intermediate,root certificate
    class trustStore trust

만약 인증서의 도메인이 실제 접속한 도메인과 다르거나, 사슬이 깨졌거나, 만료된 인증서라면 브라우저는 빨간 경고 화면을 띄웁니다. “이 사이트는 안전하지 않습니다” 같은 경고가 그 결과인데요. 공격자가 가짜 서버를 세워도 접속하려던 도메인의 정상 인증서를 제시할 수 없기 때문에, 이 단계에서 가로채기 시도가 발각됩니다.

인증서를 발급받을 때 한 가지 더 알아두면 좋은 것이 있습니다. 한 인증서가 보호할 수 있는 도메인 범위는 인증서 안의 SAN(Subject Alternative Name) 필드로 정해지는데, 여기에 여러 도메인을 함께 적어두면 한 장으로 여러 사이트를 보호할 수 있습니다. 와일드카드 인증서(*.example.com)는 한 단계 하위 도메인을 모두 묶어 보호해 주는 형태라 운영이 편하지만, 키 하나가 새면 모든 하위 도메인이 함께 영향을 받는다는 점은 기억해둘 필요가 있습니다.

HTTPS가 지켜주는 것, 그리고 못 지키는 것

HTTPS가 기본적으로 보장하는 것은 세 가지입니다. 첫째는 통신 내용이 중간에서 읽히지 않게 하는 기밀성(confidentiality), 둘째는 누군가 메시지를 살짝 바꿔도 들통나게 만드는 무결성(integrity), 셋째는 통신 상대가 그 도메인의 진짜 주인임을 확인해 주는 인증(authentication)입니다. 이 세 가지가 자물쇠 아이콘이 약속하는 내용인 셈이죠.

다만 자주 오해되는 영역이 있습니다. HTTPS는 우리가 어떤 사이트에 접속했는지 자체를 숨겨 주지는 않는데요. SNI라는 정보가 핸드셰이크 초반에 평문으로 노출되어, 적어도 어떤 도메인에 접속하는지는 같은 회선 위의 관찰자가 알 수 있습니다. 또한 HTTPS는 “사이트가 안전하다”는 뜻이 아니라 “통신이 안전하다”는 뜻이라서, 자물쇠가 켜진 사이트라도 피싱 페이지일 수 있습니다. 누군가의 사기 사이트도 인증서만 정상적으로 발급받으면 자물쇠 표시는 켜집니다.

서버에 보낸 데이터를 서버가 어떻게 보관하는지, 응용 프로그램에 버그가 없는지도 HTTPS의 책임이 아닙니다. HTTPS는 문 앞에 자물쇠를 채워 줄 뿐, 집 안에서 일어나는 일까지 막아주지는 않는다고 생각하면 쉽습니다.

반대로 의외의 보너스도 있습니다. 인증서가 도메인과 단단히 묶여 있다는 사실 덕분에, DNS 리바인딩처럼 도메인을 다른 IP로 바꿔치기하는 공격도 자연스럽게 차단됩니다. 공격자가 자기 도메인을 피해자 내부망 IP로 가리키게 해도, 그 내부 서버에는 공격자 도메인의 유효한 인증서가 없으니 TLS 핸드셰이크 단계에서 연결이 끊어지거든요.

TLS 1.2와 1.3, 더 빨라진 핸드셰이크

TLS도 HTTP처럼 버전이 진화해 왔습니다. 오랫동안 표준이었던 TLS 1.2는 안전하지만, 핸드셰이크에 왕복이 두 번 필요해서 첫 응답이 도착하기까지 약간의 지연이 있었는데요. TLS 1.3은 이 절차를 한 번의 왕복으로 줄여, 첫 요청까지 걸리는 시간을 의미 있게 단축했습니다.

또 TLS 1.3은 안전하다고 검증되지 않은 옛 알고리즘들을 명세에서 아예 빼 버렸습니다. RC4, MD5, SHA-1 같은 약한 도구를 더 이상 협상 옵션에 두지 않아, 잘못된 설정으로 보안이 약해질 여지를 줄였죠. 2018년에 RFC 8446으로 표준이 되었고, 2026년 7월에는 이를 소폭 개정한 RFC 9846이 기존 명세를 대체했습니다. 프로토콜 버전은 TLS 1.3 그대로이고 기존 구현과도 호환됩니다.

이 글에서는 HTTPS의 체감 속도와 안전한 기본값에 초점을 맞추고, 0-RTT 같은 세션 재개 방식은 TLS 글에서 따로 다룹니다.

HTTPS의 비용, 그리고 거의 사라진 비용

예전에는 “HTTPS는 느리다”는 이야기가 자주 나왔습니다. 암호화 연산이 추가되니 CPU를 더 쓰고, 핸드셰이크 때문에 첫 응답이 늦어지는 것이 사실이었거든요. 하지만 TLS는 핸드셰이크에서 세션 키를 합의한 뒤, 실제 HTTP 메시지를 빠른 대칭키 암호화로 처리합니다. 요즘 하드웨어는 AES 같은 대칭 암호 연산을 전용 명령어로 가속해서, 보통의 트래픽에서는 체감하기 어려울 만큼 비용이 작습니다.

오히려 HTTPS는 성능에 도움이 되는 면도 있습니다. HTTP/2와 HTTP/3은 사실상 HTTPS 위에서만 동작하기 때문에, HTTPS로 옮기지 않으면 새 버전의 이점을 받지 못하는데요. 브라우저가 HTTP/1.1로 떨어지면 한 페이지를 그리는 데 더 많은 연결을 써야 하니, 결과적으로는 HTTPS 쪽이 더 빨리 그려지는 경우도 흔합니다.

여기에 검색 엔진은 HTTPS를 사용하는 사이트를 살짝 우대하고, 브라우저는 HTTP 사이트에 “안전하지 않음”이라는 표시를 붙이기 시작한 지 오래되었습니다. 이제는 HTTPS를 쓰지 않을 이유를 찾는 쪽이 더 어려워진 셈입니다.

무료 인증서와 자동 갱신

HTTPS가 한때 비싸 보였던 또 다른 이유는 인증서 자체에 매년 비용이 들었기 때문입니다. 이 장벽을 거의 없앤 것이 Let’s Encrypt 같은 무료 인증 기관의 등장인데요. 도메인 소유를 자동으로 검증하는 ACME 프로토콜을 통해, 사람이 직접 손쓰지 않아도 인증서를 발급받고 갱신할 수 있게 되었습니다.

certbot으로 인증서 발급
sudo certbot --nginx -d example.com

위 명령어 한 줄이면 nginx 설정에 인증서를 자동으로 붙여 주고, 이후에는 cron이나 systemd timer가 갱신을 알아서 챙겨 주는데요. 요즘은 Caddy나 Traefik 같은 웹 서버가 처음부터 ACME 클라이언트를 내장하고 있어, 도메인만 가리키면 인증서까지 한 번에 처리해 줍니다. 처음 HTTPS를 붙여본 뒤 한 번 익숙해지면, 새 사이트를 만들 때 가장 먼저 챙기는 일 가운데 하나가 됩니다. Let’s Encrypt와 ACME가 어떻게 이 자동화를 가능하게 만들었는지가 더 궁금하다면 Let’s Encrypt 글에서 따로 정리해 두었습니다.

HSTS, 한 번 HTTPS면 끝까지 HTTPS

HTTPS를 켰다고 해서 안전이 모두 보장되는 것은 아닙니다. 공격자가 사용자의 첫 요청을 가로채 HTTP로 연결되도록 유도하면, 그 첫 통신은 여전히 평문으로 흐를 수 있는데요. 이런 SSL stripping 공격을 막기 위해 등장한 것이 HSTS(HTTP Strict Transport Security)입니다.

HSTS는 응답 헤더에 한 줄을 더하는 것으로 켤 수 있습니다.

HSTS 헤더 예시
Strict-Transport-Security: max-age=31536000; includeSubDomains

이 헤더를 한 번이라도 받은 브라우저는 지정된 기간 동안 해당 도메인에 대해 HTTP 요청을 보내지 않고, 어떤 링크가 http://로 적혀 있어도 자동으로 https://로 바꿔서 요청합니다. 첫 요청까지 안전하게 만들고 싶다면 HSTS preload 목록에 도메인을 등록할 수도 있는데, 이 목록은 브라우저에 직접 내장되어 있어 첫 방문부터 강제로 HTTPS만 사용하게 됩니다.

또 자주 마주치는 함정이 혼합 콘텐츠(mixed content)입니다. HTTPS 페이지 안에서 이미지나 스크립트를 http:// 주소로 불러오면, 브라우저가 그 자원을 차단하거나 경고를 띄우는데요. HTTPS로 옮길 때는 페이지 안의 모든 자원 URL을 함께 점검해야 깨끗하게 자물쇠가 켜집니다.

마치며

HTTPS는 새로운 프로토콜이라기보다 HTTP 위에 TLS라는 한 겹을 더한 형태입니다. 이 한 겹이 통신 내용을 가리고, 변조를 막고, 상대가 누구인지를 확인해 주는데요. TLS 1.3과 무료 인증서가 자리를 잡은 이후로는 도입 비용도 거의 사라져, “HTTPS는 기본”이라는 말이 더는 과장이 아닙니다.

다음에 자물쇠 아이콘을 보게 되면, 그 뒤에서 핸드셰이크와 인증서 체인이 어떤 일을 해주고 있는지를 한 번 떠올려보면 좋겠습니다. TLS의 자세한 동작이 궁금하다면 Cloudflare의 TLS 가이드를 추천합니다.

This work is licensed under CC BY 4.0CCBY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord