네트워크 보안의 원리 (2) - TLS와 인증서 체계 - soo:bak
작성일 :
안전한 연결이 필요한 이유
Part 1에서 대칭키 암호, 비대칭키 암호, 해시 함수 등 암호화의 수학적 기초를 살펴보았습니다. 그렇다면 이것들을 어떻게 조합하여 실제 안전한 통신을 구현할 수 있을까요?
TLS(Transport Layer Security)는 이 암호화 기법들을 조합하여 안전한 통신을 구현합니다. HTTPS의 “S”가 바로 TLS를 의미합니다.
TLS는 세 가지 문제를 해결합니다:
- 키 교환: 처음 연결하는 클라이언트와 서버가 어떻게 비밀 키를 공유하는가
- 서버 인증: 접속한 서버가 진짜 그 서버인지 어떻게 확인하는가
- 데이터 보호: 교환된 키로 데이터를 암호화하고 변조를 감지하는가
TLS의 역사
TLS는 원래 SSL(Secure Sockets Layer)이라는 이름이었습니다. 1999년에 표준화되면서 TLS로 바뀌었지만, “SSL 인증서”라는 표현은 여전히 사용됩니다. 둘은 같은 것을 의미합니다.
| 버전 | 상태 |
|---|---|
| SSL, TLS 1.0, TLS 1.1 | 취약점 발견, 사용 금지 |
| TLS 1.2 | 안전, 현재 널리 사용 |
| TLS 1.3 | 안전, 최신 버전 |
TLS 1.2 핸드셰이크
TLS 연결은 핸드셰이크(Handshake)로 시작됩니다. 악수하듯이 서로 정보를 주고받으며, 어떤 암호화 방식을 사용할지 정하고 키를 교환합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
클라이언트 서버
│ │
│ ─── 1. 지원하는 암호화 방식 목록 ───────► │
│ │
│ ◄─── 2. 선택한 암호화 방식 ────────────── │
│ ◄─── 3. 서버 인증서 (공개키 포함) ─────── │
│ ◄─── 4. 서버의 키 교환 데이터 ─────────── │
│ │
│ ─── 5. 클라이언트의 키 교환 데이터 ─────► │
│ │
│ ◄────────── 6, 7. 검증 ─────────────────► │
│ │
│ ══════════ 암호화된 통신 ═══════════════ │
클라이언트와 서버가 2번 왕복(Round Trip)해야 암호화된 통신을 시작할 수 있습니다.
각 단계를 살펴보면:
1. 클라이언트가 지원하는 암호화 방식 목록 전송
클라이언트가 “나는 이런 암호화 방식들을 지원해”라고 목록을 보냅니다.
2. 서버가 암호화 방식 선택
서버가 목록 중 하나를 선택합니다. 이 조합을 암호 스위트(Cipher Suite)라고 합니다. 예: “AES-256으로 암호화하고 SHA-256으로 해시하자”
3. 서버 인증서 전송
서버가 인증서를 보냅니다. 인증서에는 서버의 공개키가 포함되어 있습니다.
4. 서버의 키 교환 데이터 전송
Part 1에서 살펴본 Diffie-Hellman 키 교환을 위해 서버의 임시 공개키를 보냅니다.
TLS 1.2에서는 RSA 키 교환도 가능합니다. 이 경우 4단계 없이 서버 인증서의 공개키를 직접 사용합니다. 하지만 RSA 방식은 전방향 비밀성이 없어서 현재는 Diffie-Hellman(DHE/ECDHE)이 권장됩니다. TLS 1.3에서는 RSA 키 교환이 제거되어 Diffie-Hellman만 사용됩니다.
5. 클라이언트의 키 교환 데이터 전송
Diffie-Hellman 방식에서는 클라이언트도 자신의 임시 공개키를 보냅니다. 양쪽이 같은 비밀 값을 계산할 수 있습니다.
RSA 방식에서는 클라이언트가 무작위 비밀 값을 생성하고, 서버의 공개키로 암호화하여 보냅니다. 서버가 개인키로 복호화하면 같은 비밀 값을 얻습니다.
이 비밀 값으로 실제 통신에 사용할 세션 키를 만듭니다.
6. 클라이언트 검증 메시지 전송
클라이언트가 지금까지 주고받은 모든 메시지의 해시를 세션 키로 암호화하여 보냅니다.
7. 서버 검증 메시지 전송
서버도 같은 방식으로 해시를 계산하여 보냅니다. 양쪽이 계산한 값이 일치하면 중간에 변조가 없었다는 의미이고, 핸드셰이크가 완료됩니다.
TLS 1.2의 문제점
TLS 1.2 핸드셰이크는 클라이언트와 서버가 2번 왕복해야 합니다. 네트워크 지연이 100ms라면 핸드셰이크에만 200ms가 걸립니다. 모바일 네트워크에서는 더 오래 걸릴 수 있습니다.
또한 보안상 문제가 있는 방식이 여전히 허용됩니다. 앞서 설명한 RSA 키 교환은 전방향 비밀성이 없어서, 서버의 개인키가 유출되면 과거 통신을 모두 복호화할 수 있습니다. 취약점이 발견된 암호화 모드와 해시 함수도 사용 가능합니다.
TLS 1.3: 더 빠르고 안전하게
TLS 1.3은 속도와 보안 두 가지 문제를 모두 해결합니다.
더 빠르게: 핸드셰이크가 1번 왕복으로 단축
TLS 1.2에서는 암호화 방식을 정하고 나서 키 교환 데이터를 보내야 해서 2번 왕복이 필요했습니다. TLS 1.3에서는 클라이언트가 첫 메시지에 임시 공개키를 함께 보내므로 1번 왕복이면 충분합니다.
1
2
3
4
5
6
7
8
9
10
클라이언트 서버
│ │
│ ─── 암호화 방식 + 임시 공개키 ────────────► │
│ │
│ ◄─── 암호화 방식 + 임시 공개키 ──────────── │
│ ◄─── 인증서 + 검증 (암호화됨) ───────────── │
│ │
│ ─── 검증 (암호화됨) ────────────────────► │
│ │
│ ═══════════ 암호화된 통신 ═════════════════ │
네트워크 지연이 100ms라면 핸드셰이크 시간이 200ms에서 100ms로 절반이 됩니다.
0-RTT 재개
이전에 연결했던 서버라면 저장해둔 키를 사용하여 핸드셰이크 없이 바로 데이터를 보낼 수 있습니다. 이를 0-RTT(Zero Round Trip Time)라고 합니다.
단, 0-RTT에는 리플레이 공격 위험이 있습니다. 공격자가 암호화된 요청을 복사해서 서버에 다시 보내면, 서버는 이것이 새 요청인지 복사된 요청인지 구분할 수 없습니다. 예를 들어 “1만원 송금” 요청이 복사되면 송금이 두 번 실행될 수 있습니다.
그래서 0-RTT는 멱등성(Idempotency)이 있는 요청에만 사용해야 합니다. 멱등성이란 여러 번 실행해도 결과가 같은 성질입니다. 페이지 조회는 멱등하므로 괜찮지만, 결제나 송금은 멱등하지 않으므로 사용하면 안 됩니다.
취약한 방식 제거
TLS 1.3은 취약점이 발견된 오래된 방식들을 모두 제거했습니다:
- RSA 키 교환: 전방향 비밀성이 없어서 제거
- CBC 모드: 패딩 공격에 취약해서 제거
- MD5, SHA-1: 충돌 공격에 취약해서 제거
남은 암호 스위트는 안전한 것들만 있습니다:
- AES-GCM: Part 1에서 살펴본 인증된 암호화 방식
- ChaCha20-Poly1305: 모바일에 최적화된 대안
키 교환은 전방향 비밀성을 제공하는 ECDHE 또는 DHE만 허용됩니다.
인증서: 신뢰의 기반
서버가 공개키를 보내면 클라이언트는 이것을 어떻게 신뢰할 수 있을까요?
중간자 공격(Man-in-the-Middle)
공격자가 클라이언트와 서버 사이에 끼어들어 양쪽을 속일 수 있습니다.
1
2
3
4
클라이언트 ◄──► 공격자 ◄──► 서버
클라이언트는 공격자를 서버로 인식
서버는 공격자를 클라이언트로 인식
공격자가 자신의 공개키를 서버의 것이라고 속이면, 클라이언트는 공격자의 키로 암호화합니다. 공격자는 이를 복호화해서 내용을 확인한 뒤, 진짜 서버의 키로 다시 암호화하여 전달합니다. 양쪽 모두 정상적인 통신이라고 생각하므로 공격을 알아채지 못합니다.
이 공격을 막으려면 서버의 공개키가 진짜 그 서버의 것임을 확인할 방법이 필요합니다. 이것이 인증서의 역할입니다.
PKI: 공개키 기반 구조
“이 공개키가 정말 이 서버의 것인가?”를 확인하려면 신뢰할 수 있는 제3자가 필요합니다.
PKI(Public Key Infrastructure)는 이 문제를 해결하는 체계입니다. 인증 기관(CA, Certificate Authority)이라는 신뢰할 수 있는 기관이 “이 공개키는 이 서버의 것이 맞다”라고 서명해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
웹사이트 운영자 CA 사용자
│ │ │
│ ── 공개키 + 도메인 정보 ──► │ │
│ │ │
│ 도메인 소유권 확인 │
│ │ │
│ ◄── 인증서 발급 ────────── │ │
│ (공개키 + 도메인 + CA 서명) │
│ │
│ ─────────────── 인증서 전송 ────────────────────────────► │
│ │
│ CA의 공개키로 서명 검증
│ 유효하면 공개키 신뢰
X.509 인증서 구조
인증서의 표준 형식은 X.509입니다. 인증서에 포함된 정보를 살펴보면:
- 발급 대상: example.com
- 발급자: Let’s Encrypt (CA)
- 유효 기간: 2026-01-01 ~ 2026-03-31
- 공개키: 서버의 공개키
- 대체 도메인(SAN): www.example.com, api.example.com
- CA의 서명: 위 내용 전체에 대한 CA의 디지털 서명
대체 도메인(Subject Alternative Name, SAN)을 사용하면 하나의 인증서로 여러 도메인을 사용할 수 있습니다. 위 예시에서는 example.com, www.example.com, api.example.com 세 도메인이 모두 같은 인증서로 보호됩니다.
인증서 체인
그런데 CA의 공개키는 어떻게 신뢰할 수 있을까요? 더 상위의 CA가 서명합니다. 이렇게 서명이 연결된 구조를 인증서 체인(Certificate Chain)이라고 합니다.
1
루트 CA ──서명──► 중간 CA ──서명──► 서버 인증서
그러면 최상위에 있는 루트 CA는 누가 서명하나요? 루트 CA는 자기 자신을 서명합니다. 이를 자체 서명(Self-signed)이라고 합니다.
하지만 자체 서명만으로는 신뢰할 수 없습니다. 누구나 “나는 신뢰할 수 있는 CA다”라고 주장하며 자체 서명 인증서를 만들 수 있기 때문입니다. 그래서 운영체제와 브라우저 제조사(Microsoft, Apple, Google, Mozilla 등)가 신뢰할 수 있는 루트 CA를 직접 심사합니다. 심사를 통과한 루트 CA의 공개키만 운영체제와 브라우저에 미리 설치됩니다.
이렇게 미리 설치된 루트 CA 목록을 신뢰 저장소(Trust Store)라고 합니다. Windows, macOS, iOS, Android, Firefox 등은 각각 자체 신뢰 저장소를 관리하며, 약 100~200개의 루트 CA가 포함되어 있습니다.
HTTPS 연결 시 클라이언트는 서버가 보낸 인증서의 서명을 신뢰 저장소에 있는 CA 공개키로 검증합니다. 새 컴퓨터나 스마트폰에는 신뢰 저장소가 이미 설치되어 있으므로, 사용자가 CA 공개키를 직접 구하지 않아도 됩니다.
인증서 검증 과정
브라우저는 서버에서 받은 인증서를 다음 순서로 검증합니다.
1. 체인 구축: 서버 인증서 → 중간 CA → 루트 CA까지 체인을 구성합니다.
2. 서명 검증: 각 인증서의 서명을 상위 CA의 공개키로 검증합니다.
3. 신뢰 루트 확인: 체인의 최상위가 신뢰 저장소에 있는 루트 CA인지 확인합니다.
4. 유효 기간 확인: 현재 시간이 인증서의 유효 기간 내인지 확인합니다.
5. 도메인 일치 확인: 인증서의 도메인(SAN)이 접속하려는 도메인과 일치하는지 확인합니다.
6. 폐기 여부 확인: 인증서가 만료 전에 폐기되지 않았는지 확인합니다.
인증서 폐기는 개인키 유출 등의 이유로 만료 전에 인증서를 무효화하는 것입니다. 확인 방법은 두 가지가 있습니다:
- CRL(Certificate Revocation List): CA가 주기적으로 발행하는 폐기 목록을 다운로드
- OCSP(Online Certificate Status Protocol): CA에 실시간으로 상태를 질의
인증서 투명성 (Certificate Transparency)
CA가 해킹당하거나 실수로 잘못된 인증서를 발급하면 어떻게 될까요?
2011년에 DigiNotar라는 CA가 해킹당해서 google.com에 대한 인증서가 공격자에게 발급되었습니다. 공격자는 이 인증서로 중간자 공격을 수행했고, 이란 사용자들의 Gmail이 감청되었습니다.
인증서 투명성(Certificate Transparency, CT)은 이런 잘못된 발급을 탐지하기 위한 시스템입니다. CA가 인증서를 발급할 때 공개 로그에 기록해야 합니다. 이 로그는 누구나 확인할 수 있으므로, 도메인 소유자는 자신의 도메인에 대해 발급된 모든 인증서를 모니터링할 수 있습니다.
Google이 DigiNotar 사건 이후 CT를 개발했고, Chrome은 2018년부터 CT 로그에 기록되지 않은 인증서를 신뢰하지 않습니다. 다른 브라우저들도 비슷한 정책을 적용하고 있습니다.
Let’s Encrypt: 무료 자동화 인증서
예전에는 인증서 발급에 비용(연간 수십~수백 달러)이 들었고, 수동으로 서류를 제출해야 했습니다. Let’s Encrypt는 이 과정을 무료로 자동화한 CA입니다.
Let’s Encrypt는 ACME(Automated Certificate Management Environment) 프로토콜로 도메인 소유권을 자동 검증합니다. 서버가 Let’s Encrypt에 인증서를 요청하면, Let’s Encrypt가 “이 도메인의 특정 경로에 파일을 올려라” 같은 과제를 제시합니다. 서버가 과제를 완료하면 도메인 소유권이 증명되고, 인증서가 발급됩니다.
Let’s Encrypt 인증서는 유효 기간이 90일로 짧습니다. 짧은 유효 기간은 개인키 유출 시 피해를 줄여주지만, 수동 갱신은 번거롭습니다. ACME를 사용하면 갱신도 자동화되므로 문제가 되지 않습니다.
Let’s Encrypt 덕분에 개인 블로그부터 대형 서비스까지 HTTPS를 쉽게 적용할 수 있게 되었습니다.
인증서의 종류
인증서는 검증 수준에 따라 세 가지로 나뉩니다.
DV(Domain Validation): 도메인 소유권만 확인합니다. Let’s Encrypt가 발급하는 인증서가 DV입니다. 발급이 빠르고 무료 또는 저렴합니다.
OV(Organization Validation): 도메인 소유권과 함께 조직의 실제 존재를 확인합니다. 회사 등록 서류를 검토하며, 인증서에 조직 이름이 포함됩니다.
EV(Extended Validation): 가장 엄격한 검증입니다. 법적 실체, 물리적 존재, 운영 상태까지 확인합니다. 예전에는 EV 인증서가 있으면 브라우저 주소창이 녹색으로 표시되었지만, 사용자들이 이를 보안 지표로 인식하지 못해서 현재는 녹색 표시가 대부분 사라졌습니다. EV 인증서 자체는 여전히 발급됩니다.
세 종류 모두 암호화 강도는 동일합니다. 차이는 “이 도메인을 누가 운영하는가”를 얼마나 엄격하게 확인했는지입니다.
HSTS: 다운그레이드 방지
사용자가 http://example.com으로 접속하면 서버가 https://로 리다이렉트합니다. 하지만 첫 번째 HTTP 요청은 암호화되지 않습니다. 공격자가 이 요청을 가로채서 리다이렉트를 차단하면, 사용자는 계속 HTTP로 통신하게 됩니다. 이를 SSL Stripping 공격이라고 합니다.
HSTS(HTTP Strict Transport Security)는 브라우저에게 “이 도메인은 항상 HTTPS로 접속하라”고 알려주는 방식으로 이 공격을 방지합니다.
서버가 HTTPS 응답에 다음 헤더를 포함하면:
1
Strict-Transport-Security: max-age=31536000; includeSubDomains
브라우저는 1년(31536000초) 동안 이 도메인에 대해 HTTP 요청을 자동으로 HTTPS로 변환합니다. 인증서 오류가 발생해도 예외를 허용하지 않고 연결을 거부합니다.
하지만 HSTS는 한 번 HTTPS로 접속한 후에야 적용됩니다. 첫 방문은 여전히 HTTP일 수 있습니다. HSTS Preload는 이 문제를 해결합니다. 브라우저에 HSTS 도메인 목록을 미리 포함하여, 첫 방문부터 HTTPS를 강제합니다.
TLS가 보호하는 것과 보호하지 않는 것
TLS가 보호하는 것:
- 데이터의 기밀성: 제3자가 내용을 볼 수 없음
- 데이터의 무결성: 중간에서 변조되면 감지됨
- 서버 인증: 접속한 서버가 진짜인지 확인
TLS가 보호하지 않는 것:
- 메타데이터: 접속한 IP, 시간, 데이터 크기는 노출됨
- 접속하려는 도메인(SNI): 핸드셰이크 시 평문으로 전송됨
- 클라이언트 신원: TLS는 기본적으로 서버만 인증하고 클라이언트는 인증하지 않음
SNI(Server Name Indication)는 여러 도메인이 같은 IP를 공유할 때 필요합니다. 핸드셰이크 시 클라이언트가 “나는 example.com에 접속하려 한다”고 알려주면, 서버가 해당 도메인의 인증서를 선택할 수 있습니다. 이 정보는 암호화 전에 전송되므로 네트워크를 감시하는 사람이 사용자가 어떤 사이트에 접속하는지 알 수 있습니다.
Encrypted Client Hello(ECH)는 SNI까지 암호화하는 TLS 1.3 확장입니다. 아직 표준화 진행 중이며, 일부 브라우저와 서비스에서 시험 지원하고 있습니다.
마무리
이 글에서는 TLS와 인증서 체계를 살펴보았습니다.
- TLS 핸드셰이크: 클라이언트와 서버가 암호화 방식을 정하고 키를 교환하는 과정
- TLS 1.3: 핸드셰이크가 1번 왕복으로 단축되고, 취약한 암호화 방식이 제거됨
- 인증서와 CA: 서버의 공개키가 진짜임을 인증 기관(CA)이 서명으로 보증
- 인증서 체인: 서버 인증서 → 중간 CA → 루트 CA로 이어지는 신뢰 구조
- 신뢰 저장소: 운영체제와 브라우저에 미리 설치된 루트 CA 목록
- Let’s Encrypt: 무료 자동화 인증서로 HTTPS 도입 비용을 낮춤
- HSTS: HTTP 접속을 HTTPS로 강제하여 다운그레이드 공격 방지
Part 3에서는 네트워크 계층별 공격과 방어를 다룹니다.
관련 글