| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- golang
- docker
- Java
- go
- minikube
- Claude
- 코틀린 코루틴의 정석
- PETERICA
- 바이브코딩
- tucker의 go 언어 프로그래밍
- kotlin coroutine
- 티스토리챌린지
- 기록으로 실력을 쌓자
- Rag
- MySQL
- Network
- CKA
- 공부
- LLM
- AI
- kotlin
- AWS EKS
- Spring
- CloudWatch
- CKA 기출문제
- 오블완
- SRE
- Kubernetes
- 정보처리기사 실기 기출문제
- aws
- Today
- Total
피터의 개발이야기
[Network] 23. 우체부를 믿지 않고도 편지 내용을 지킬 수 있을까? 본문

목차: [Network] TCP/IP를 우편 시스템으로 이해하기
TCP/IP를 우편 시스템으로 이해하기 — Part 7. 인터넷의 주소를 함께 쓰고 경계를 지키는 법
ㅁ 들어가며
Firewall이 HTTPS 통신을 허용했다.
Packet은 집의 Router와 ISP, 여러 중간 Router를 지나 Server에 도착한다.
그 경로의 모든 장비를 믿어야만 Password와 결제 정보를 보낼 수 있다면 인터넷은 안전한 업무 공간이 되기 어렵다.
경로에서 일어날 수 있는 위협을 세 가지로 나누어 보자.
도청
→ 편지 내용을 몰래 읽음
변조
→ 전달 중 내용을 바꿈
사칭
→ 공격자가 진짜 Server인 척함
암호화 하나만으로 세 문제를 모두 해결할 수는 없다.
Confidentiality
→ 허가되지 않은 사람이 내용을 읽지 못하게 함
Integrity
→ 전달 중 내용이 바뀌었는지 확인함
Authentication
→ 통신 상대의 Identity를 확인함
Transport Layer Security(TLS)는 이 속성을 결합해 신뢰할 수 없는 Network 위에 보호된 Application Channel을 만든다.
이번 글에서는 TLS 1.3의 ClientHello, ServerHello, Key Exchange, Certificate Chain, CertificateVerify, Finished, AEAD, Session Resumption을 따라가며 HTTPS가 해결하는 문제와 해결하지 않는 문제를 구분한다.
ㅁ 자물쇠만 채우면 상대를 믿을 수 있을까?
Client와 공격자가 암호화된 연결을 만들었다고 하자.
그 연결 안의 내용은 중간의 다른 사람에게 보이지 않을 수 있다.
하지만 Client가 공격자를 진짜 Bank Server로 착각했다면 안전한 암호화가 공격자와의 비밀 통로를 만들어 준 셈이다.
Client ↔ 암호화 ↔ Attacker
Encryption 성공
Server Authentication 실패
반대로 진짜 Server의 Certificate를 확인했지만 Application Data를 평문으로 보내면 경로에서 내용이 노출될 수 있다.
TLS가 여러 단계를 사용하는 이유다.
상대가 제시한 Public Key는 누구의 것인가?
그 Public Key에 대응하는 Private Key를 실제로 가지고 있는가?
양쪽이 만든 Session Key가 같은가?
Handshake 메시지가 중간에서 바뀌지 않았는가?
이후 Application Data를 안전하게 보호할 수 있는가?
하나의 자물쇠가 아니라 서로 다른 증명을 연결해야 한다.
ㅁ HTTPS는 HTTP와 다른 Transport Protocol일까?
일반적인 HTTP/1.1과 HTTP/2의 HTTPS 연결은 TCP 위에 TLS를 놓고 그 안에 HTTP를 전달한다.
HTTP Application Message
↓
TLS Record
↓
TCP Byte Stream
↓
IP Packet
연결 순서는 다음과 같다.
1. DNS로 Server Address 확인
2. TCP 3-Way Handshake
3. TLS Handshake
4. 보호된 HTTP Request와 Response
TCP Handshake 성공은 TLS 성공이 아니고, TLS 성공은 HTTP 요청 처리 성공이 아니다.
TCP 성공 + TLS 인증서 오류
→ Port와 경로는 열렸지만 보안 연결 실패
TLS 성공 + HTTP 500
→ 보호된 Channel은 생겼지만 Application 처리 실패
HTTP/3는 TCP 대신 UDP 기반 QUIC을 사용하고 TLS 1.3 Handshake를 QUIC 연결 설정에 통합한다.
계층의 배치는 다르지만 상대 인증, Key 합의와 보호된 Application Data라는 목표는 같다.
ㅁ TLS는 왜 비대칭키와 대칭키를 함께 사용할까?
Public Key Cryptography는 상대 인증과 Key Agreement에 유용하다.
하지만 대용량 Application Data를 모두 Public Key 연산으로 처리하는 것은 비효율적이다.
대칭키 암호는 같은 Secret Key를 공유한 뒤 많은 Data를 빠르게 보호하는 데 적합하다.
TLS는 역할을 나눈다.
Public Key Cryptography
├─ Certificate의 Signature 검증
├─ Server의 Private Key 소유 증명
└─ Ephemeral Key Agreement와 인증 연결
Symmetric Cryptography
├─ Handshake Message 보호
└─ Application Data의 Encryption과 Integrity
Certificate가 Web Page 전체를 직접 암호화하는 것도 아니고,
Server Private Key가 매 TCP Segment를 암호화하는 것도 아니다.
Handshake에서 공유 Secret을 만들고 그로부터 방향별 Traffic Key를 파생한 뒤 대칭키로 TLS Record를 보호한다.
ㅁ TLS Handshake 전에 TCP는 무엇을 끝냈을까?
TCP 기반 TLS를 가정하자.
먼저 TCP 연결이 만들어진다.
Client → Server : SYN
Server → Client : SYN-ACK
Client → Server : ACK
이 단계가 확인하는 것은 TCP Endpoint와 양방향 Byte Stream이다.
아직 다음은 확인하지 않았다.
Server Certificate가 유효한가?
접속한 Hostname과 Certificate가 일치하는가?
Application Data를 암호화할 Key가 있는가?
HTTP Message가 보호되는가?
TCP 연결 위에서 TLS Handshake가 별도로 시작되는 이유다.
ㅁ 첫 번째 보안 인사 — ClientHello
Client는 ClientHello로 자신이 지원하는 조건과 Key Agreement 재료를 보낸다.
TLS 1.3의 대표적인 내용은 다음과 같다.
ClientHello
├─ Supported Versions
├─ Cipher Suites
├─ Supported Groups
├─ Signature Algorithms
├─ Key Share
├─ Server Name Indication(SNI)
├─ Application-Layer Protocol Negotiation(ALPN) 제안
└─ Random과 Extension
ㅇ Supported Versions
Client가 사용할 수 있는 TLS Version을 알린다.
ㅇ Cipher Suites
TLS 1.3 Cipher Suite는 주로 Application Data를 보호할 AEAD 알고리즘과 Hash 조합을 나타낸다.
TLS 1.2 시대의 이름처럼 Certificate Signature와 Key Exchange 방식까지 Cipher Suite 이름 하나에 모두 넣는 구조로 이해하면 안 된다. Supported Groups와 Signature Algorithms 같은 Extension이 별도로 역할을 나눈다.
ㅇ Key Share
Client가 Ephemeral Diffie-Hellman 계열 Key Agreement에 사용할 Public 값을 보낸다.
Private 값 자체를 Network로 보내는 것은 아니다.
ㅇ SNI
하나의 IP Address에서 여러 Domain을 서비스할 때 Client가 원하는 Server Name을 알릴 수 있다.
일반적인 ClientHello의 SNI는 중간 장비에 보일 수 있다. Encrypted ClientHello(ECH)를 사용하는 구성에서는 ClientHello의 민감한 부분을 암호화할 수 있지만 지원과 구성에 따라 관찰 가능성은 달라진다.
ㅇ ALPN
Client가 h2, http/1.1처럼 TLS 위에서 사용할 Application Protocol 후보를 제안한다.
Port 443만으로 HTTP Version을 결정하는 것이 아니다.
ㅁ 두 번째 보안 인사 — ServerHello
Server는 Client가 제시한 조건에서 사용할 Version, Cipher Suite와 Key Share를 선택해 ServerHello를 보낸다.
ServerHello
├─ Selected TLS Version
├─ Selected Cipher Suite
├─ Server Key Share
└─ Random 등
Client와 Server는 상대의 Public Key Share와 자신의 Ephemeral Private 값을 이용해 같은 Shared Secret을 계산할 수 있다.
Client가 계산한 Shared Secret
= Server가 계산한 Shared Secret
Network로 Shared Secret 자체를 전송하지 않음
공격자가 Public Key Share를 Capture해도 Ephemeral Private 값을 모르면 같은 Secret을 계산하기 어렵도록 설계한다.
TLS 1.3에서는 ServerHello 뒤의 Handshake Message가 파생된 Handshake Traffic Key로 보호되기 시작한다.
Passive Capture에서 TLS 1.3 Certificate가 평문으로 보이지 않을 수 있는 이유다.
ㅁ Server가 선택한 Application Protocol은 어디에서 알릴까?
Server는 EncryptedExtensions에서 ClientHello의 여러 Extension에 대한 선택 결과를 전달한다.
예를 들어 ALPN으로 선택한 Application Protocol이 들어갈 수 있다.
ClientHello ALPN Offer
├─ h2
└─ http/1.1
EncryptedExtensions ALPN Selection
└─ h2
TLS 1.3에서는 EncryptedExtensions가 ServerHello 이후 암호화된 Handshake 영역에 있다.
Client가 HTTP/2를 원했다고 항상 h2가 선택되는 것은 아니다.
양쪽이 공통으로 지원하고 Server Policy가 허용한 결과가 선택된다.
ㅁ Certificate에는 무엇이 들어 있을까?
Server는 자신의 Identity와 Public Key를 연결한 X.509 Certificate와 필요한 Chain을 보낸다.
Leaf Certificate의 대표 정보는 다음과 같다.
Subject 또는 Identity 정보
Subject Alternative Name(SAN)
Public Key
Issuer
Serial Number
Validity: Not Before / Not After
Key Usage와 Extended Key Usage
Issuer의 Digital Signature
Certificate에는 Server의 Public Key가 들어 있다.
Server의 Private Key는 들어 있지 않으며 외부로 보내면 안 된다.
Certificate 배포 가능
Private Key 비밀 유지
Certificate 파일과 Private Key 파일을 같은 것으로 생각하면 안 된다.
ㅁ 인증서 체인은 왜 여러 장일까?
Web PKI의 일반적인 Chain은 다음과 같다.
Root CA Certificate
↓ 서명
Intermediate CA Certificate
↓ 서명
Server Leaf Certificate
Client는 Server가 보낸 Leaf와 Intermediate를 따라가며 자신이 이미 신뢰하는 Root CA에 도달할 수 있는지 확인한다.
Server는 보통 Leaf와 필요한 Intermediate를 보내고 Root Certificate는 보내지 않거나 보내도 신뢰의 근거가 되지 않는다.
Root를 신뢰하는 이유는 Network에서 방금 받았기 때문이 아니라
Client 운영체제나 Browser의 Trust Store에 미리 신뢰 대상으로 설치되어 있기 때문이다.
Server가 Root를 함께 보냄
≠ Client가 그 Root를 자동 신뢰
누구나 자기 이름으로 Root와 Certificate를 만들 수 있다.
중요한 것은 Client가 어떤 Trust Anchor를 어떤 절차로 신뢰하도록 구성했는가다.
ㅁ Certificate Signature는 무엇을 증명할까?
Intermediate CA가 Server Certificate의 내용을 자신의 Private Key로 서명했다고 하자.
Client는 Intermediate Certificate의 Public Key로 Signature를 검증한다.
검증이 성공하면 다음을 확인할 수 있다.
Certificate 내용이 서명 후 바뀌지 않았다.
해당 Intermediate의 Private Key 소유자가 이 Certificate를 발급했다.
그러나 Signature 하나만 맞는다고 검증이 끝나지 않는다.
- Issuer Chain이 신뢰하는 Root까지 이어지는가?
- CA Certificate가 Certificate 발급 용도로 허용되어 있는가?
- Name Constraints와 Policy가 적용되는가?
- Certificate가 현재 유효 기간 안에 있는가?
- Server Authentication 용도로 사용할 수 있는가?
- 접속한 Hostname과 SAN이 일치하는가?
- 폐기된 Certificate인지 확인할 정책은 무엇인가?
Certificate Validation은 “서명 수학 한 번”이 아니라 여러 조건을 함께 검증하는 과정이다.
ㅁ Hostname은 Certificate의 어디와 비교할까?
Client가 https://shop.example에 접속했다고 하자.
Certificate가 신뢰되는 CA에서 발급되었어도 bank.example만을 위한 Certificate라면 사용할 수 없다.
Client는 자신이 접속하려던 Reference Hostname을 Certificate의 Subject Alternative Name(SAN)과 비교한다.
접속 Hostname: shop.example
SAN: DNS:shop.example
→ 일치 가능
SAN: DNS:bank.example
→ 불일치
오래된 Common Name만 보는 방식에 의존하지 않고 SAN을 기준으로 검증하는 것이 현대적인 동작이다.
Wildcard에도 범위가 있다.
*.example.com
→ api.example.com에는 일치 가능
→ deep.api.example.com까지 모두 일치한다고 단정할 수 없음
DNS가 공격받아 다른 IP로 연결되더라도 공격자가 해당 Hostname에 유효한 Certificate와 Private Key를 제시하지 못하면 TLS Hostname 검증에서 막을 수 있다.
ㅁ 유효 기간과 Clock은 왜 중요할까?
Certificate에는 Not Before와 Not After가 있다.
현재 시각 < Not Before
→ 아직 유효하지 않음
현재 시각 > Not After
→ 만료됨
Client System Clock이 크게 틀리면 정상 Certificate도 아직 유효하지 않거나 만료된 것으로 판단할 수 있다.
인증서 오류를 조사할 때 Server 설정뿐 아니라 Client의 시간 동기화도 확인해야 하는 이유다.
유효 기간이 남았어도 Private Key 유출이나 잘못된 발급 때문에 Certificate를 조기에 폐기해야 할 수 있다. CRL과 OCSP 같은 Revocation 메커니즘이 있지만 Client Policy, Network 도달성과 실패 처리 방식이 달라 단순한 한 단계로 끝나지 않는다.
ㅁ Certificate만 보내면 Private Key 소유를 증명할까?
Certificate는 Public 정보이므로 누구나 복사할 수 있다.
공격자가 진짜 Server의 Certificate 파일만 복사해 보내는 것을 막으려면 대응하는 Private Key를 가지고 있음을 증명해야 한다.
TLS 1.3의 CertificateVerify가 이 역할을 한다.
Server는 지금까지의 Handshake Transcript에 관련된 값을 자신의 Certificate Private Key로 서명한다.
Client는 Certificate의 Public Key로 Signature를 검증한다.
Certificate 복사본만 가진 공격자
→ Private Key가 없어 유효한 CertificateVerify 생성 불가
Certificate Chain 검증은 “이 Public Key가 해당 Hostname과 연결되어 신뢰되는가?”를 확인하고,
CertificateVerify는 “상대가 그 Public Key에 대응하는 Private Key를 실제로 가지고 있는가?”를 확인한다.
ㅁ Finished는 무엇을 마지막으로 확인할까?
Server와 Client는 지금까지 주고받은 Handshake Message의 Transcript Hash와
파생된 Secret을 이용해 Finished Message를 만든다.
상대가 Finished를 올바르게 검증하면 다음 단서를 얻는다.
상대도 올바른 Handshake Secret을 계산했다.
Handshake Transcript가 양쪽에서 동일하다.
중간에서 Version, Cipher, Key Share 등이 몰래 바뀌지 않았다.
Server의 Certificate Signature만 검증하고 Finished를 생략하면
Key Agreement와 전체 Handshake의 무결성을 끝까지 묶을 수 없다.
단순화한 TLS 1.3 Handshake는 다음과 같다.
Client Server
ClientHello
Key Share, Version, Cipher, SNI, ALPN ----------->
ServerHello
Selected Key Share <---
EncryptedExtensions <---
Certificate <---
CertificateVerify <---
Finished <---
Certificate Chain, Hostname,
CertificateVerify, Finished 검증
Finished ---------------------------------------->
<========== Protected Application Data ==========>
실제 Handshake에는 선택한 인증 방식, Resumption과 Client Authentication 등에 따라 Message가 달라질 수 있다.
ㅁ 중간 공격자가 Key Share를 바꾸면 어떻게 될까?
공격자가 ClientHello와 ServerHello 사이에 들어와 서로 다른 Key Agreement를 만들려 한다고 하자.
Client ↔ Attacker ↔ Server
Key Agreement만 있고 Authentication이 없다면 Client와 Server가 각각 공격자와 Secret을 만들 위험이 있다.
TLS는 Server Certificate의 Private Key로 Handshake Transcript에 서명하고
Finished로 전체 Transcript와 파생 Secret을 검증한다.
공격자가 Key Share나 선택 Parameter를 바꾸면 유효한 CertificateVerify와 Finished를 함께 만들어야 한다.
진짜 Server의 Private Key와 Handshake Secret이 없으면 검증을 통과시키기 어렵다.
Key Agreement
+ Certificate Authentication
+ Transcript Integrity
= Active Man-in-the-Middle 방어
각 기능이 따로가 아니라 하나의 Handshake Transcript에 묶이는 것이 중요하다.
ㅁ Forward Secrecy는 무엇을 지킬까?
TLS 1.3의 일반적인 Ephemeral Diffie-Hellman Key Agreement에서는 연결마다 일시적인 Private 값을 만든다.
Handshake가 끝난 뒤 이 Ephemeral Secret을 안전하게 폐기했다고 하자.
나중에 Server Certificate의 장기 Private Key가 유출되어도
과거에 Capture한 Session의 Key를 장기 Key만으로 복원하기 어렵게 한다.
이를 Forward Secrecy라고 한다.
장기 Certificate Private Key
→ Server Identity 서명에 사용
연결별 Ephemeral Key
→ Session Shared Secret 생성
Forward Secrecy가 Endpoint Memory 탈취, Session Key Log 유출과 Application 평문 저장까지 막아 주는 것은 아니다.
Ephemeral Key 생성과 폐기, Random Number 품질과 Endpoint 보안이 함께 필요하다.
ㅁ Application Data는 어떤 방식으로 보호할까?
Handshake가 끝나면 양쪽은 파생된 방향별 Application Traffic Key로 TLS Record를 보호한다.
TLS 1.3은 Authenticated Encryption with Associated Data(AEAD) 알고리즘을 사용한다.
대표적인 역할은 다음과 같다.
Plaintext
↓ Encryption
Ciphertext
Record Metadata와 Ciphertext
↓ Authentication Tag 검증
변조 여부 확인
AES-GCM이나 ChaCha20-Poly1305 같은 AEAD 방식은 기밀성과 무결성 보호를 결합한다.
공격자가 Ciphertext의 Bit를 바꾸면 Receiver의 Authentication Tag 검증이 실패하고 정상 Application Data로 전달되지 않는다.
TCP Checksum도 전송 오류를 탐지하지만 공격자가 다시 계산할 수 있으므로 암호학적 무결성을 제공하지 않는다.
TLS Integrity는 Secret Key를 모르는 공격자가 유효한 Record를 위조하기 어렵게 만든다.
ㅁ TLS Record와 TCP Segment는 같은 크기일까?
TLS는 TCP의 Byte Stream 위에서 자체 Record를 만든다.
HTTP Message
→ TLS Record 하나 또는 여러 개
→ TCP Segment 하나 또는 여러 개
→ IP Packet
하나의 TLS Record가 여러 TCP Segment에 나뉠 수 있고
여러 작은 Record가 전송 과정에서 하나의 큰 Host Buffer처럼 관찰될 수도 있다.
TLS Record Boundary
≠ TCP Segment Boundary
≠ HTTP Message Boundary
Packet Capture에서 TCP 한 줄과 TLS Message 하나를 1:1로 대응시키면 안 된다.
TCP는 순서 있는 Byte를 전달하고 TLS Record Layer가
그 Byte에서 Record를 복원해 인증한 뒤 Application에 평문을 제공한다.
ㅁ Client도 Certificate를 보여줄 수 있을까?
일반적인 Web HTTPS에서는 Server가 Certificate로 자신을 인증하고
사용자는 그 뒤 Password, Cookie, Token 등으로 Application 인증을 수행한다.
TLS는 Client Certificate를 요청해 양쪽을 인증할 수도 있다.
이를 Mutual TLS(mTLS)라고 한다.
Server Certificate
→ Client가 Server Identity 검증
Client Certificate
→ Server가 Client Identity 검증
mTLS에서 Client도 Certificate Chain과 Private Key 소유를 증명한다.
그러나 Certificate가 곧 최종 업무 권한은 아니다.
mTLS Authentication
→ 연결 주체 확인
Application Authorization
→ 해당 주체가 어떤 API와 Data를 사용할 수 있는지 결정
인증과 권한 부여를 구분해야 한다.
ㅁ 한 번 연결할 때마다 전체 Handshake를 반복할까?
TLS는 이전 Session에서 만든 정보를 이용해 다음 연결을 더 빠르게 시작할 수 있다.
TLS 1.3의 Session Resumption은 Server가 제공한 Ticket과 PSK(Pre-Shared Key) 관련 정보를 사용할 수 있다.
첫 연결
→ Full Handshake와 Server Certificate 검증
→ Resumption 정보 획득
다음 연결
→ PSK 기반 Resumed Handshake 가능
Resumption도 새 연결의 Key를 파생하고 Handshake 무결성을 확인한다.
과거 Application Traffic Key를 그대로 계속 재사용한다는 뜻은 아니다.
Ticket의 수명, Server Key Rotation, Cluster 간 State와 Client Cache 정책이 Resumption 가능 여부에 영향을 준다.
ㅁ 0-RTT Data는 왜 모든 요청에 쓰면 안 될까?
TLS 1.3 Resumption에서는 조건이 맞으면 Client가 Handshake 완료 전에 Early Data, 즉 0-RTT Data를 보낼 수 있다.
지연을 줄일 수 있지만 Replay 위험이 있다.
공격자가 Capture한 Early Data를 Server나 Cluster의 다른 Node에 다시 전달할 수 있다.
GET /static-image
→ 반복되어도 영향이 작도록 설계 가능
POST /transfer-money
→ Replay되면 업무가 중복 실행될 위험
0-RTT를 사용하려면 Application 요청의 Idempotency와 Anti-Replay 설계를 함께 고려해야 한다.
TLS가 Record를 암호화했다는 사실만으로 업무 요청이 Exactly-Once 실행되는 것은 아니다.
ㅁ TLS가 있어도 Replay와 중복 처리가 남을까?
정상적인 TLS Record Layer는 연결 안의 Record 순서와 무결성을 보호한다.
그러나 사용자가 응답을 받지 못해 Application 요청 전체를 새 연결에서 다시 보내는 문제는 별도다.
Client가 결제 요청 전송
Server가 처리 후 응답
응답이 Client에 도착하기 전 연결 종료
Client가 새 연결에서 재시도
두 TLS 연결이 모두 안전해도 결제가 두 번 처리될 수 있다.
Application은 Request ID, Idempotency Key, Transaction 상태로 중복 실행을 제어해야 한다.
TLS의 Integrity와 Application의 Exactly-Once 의미를 혼동하면 안 된다.
ㅁ TLS를 종료하는 Load Balancer 뒤도 계속 암호화될까?
TLS는 두 TLS Endpoint 사이의 구간을 보호한다.
Load Balancer나 Reverse Proxy가 TLS를 종료하면 Client가 만든 보호 구간은 그 장비에서 끝난다.
Client
║ TLS 보호 구간 1
Load Balancer
│ Plain HTTP 또는
║ 새 TLS 보호 구간 2
Application Server
Backend로 평문을 보내면 Client와 Load Balancer 사이만 TLS로 보호된다.
Backend에도 TLS를 사용하면 별도의 Certificate, Key, Trust와 관찰 지점을 가진 두 보안 연결이 생긴다.
End-to-End라는 표현
→ 실제 TLS Endpoint가 어디인지 확인해야 함
TLS Inspection Proxy도 Client와 원래 Server 사이의 하나의 TLS Channel을 그대로 들여다보는 것이 아니다.
Client 쪽 연결을 종료하고 자신의 Certificate를 제시한 뒤 Server와 새 TLS 연결을 만드는 방식이 일반적이다.
Client가 조직의 Inspection CA를 신뢰하도록 구성되어 있어야 하며 Proxy가 평문과 Private Key 관련 책임을 갖게 된다.
ㅁ TLS가 숨기지 못하는 정보는 무엇일까?
TLS는 Application Payload와 보호 대상 Handshake Message를 암호화하지만
Packet 전달에 필요한 모든 Metadata를 없애지는 않는다.
중간 Network에서 일반적으로 관찰 가능한 정보가 있다.
Source와 Destination IP
TCP 또는 UDP Port
Packet 크기와 방향
전송 시간과 연결 지속 시간
TLS 사용 사실과 일부 Handshake Metadata
일반적인 TLS ClientHello의 SNI와 ALPN Offer도 보일 수 있다.
ECH가 적용된 구성에서는 ClientHello 내부 정보의 노출 범위가 달라진다.
DNS 질의를 평문으로 보냈다면 접속 전에 조회한 Domain도 Network에서 관찰될 수 있다.
TLS와 DNS Privacy는 별도의 구간과 Protocol 문제다.
Traffic Analysis까지 완전히 막는 익명화 Protocol로 TLS를 이해하면 안 된다.
ㅁ TLS가 해결하지 않는 문제는 무엇일까?
HTTPS가 성공해 Browser에 자물쇠가 보인다고 Site의 모든 행동이 안전하다는 뜻은 아니다.
TLS가 해결하지 않는 대표 문제는 다음과 같다.
- Server Application의 SQL Injection과 권한 검증 오류
- 사용자가 공격자의 유사 Domain을 직접 방문한 Phishing
- Client나 Server Endpoint의 Malware와 Memory 탈취
- Server가 받은 평문 Data의 안전하지 않은 저장
- 인증된 사용자의 과도한 권한
- DDoS와 Service Availability
- Traffic 크기와 시간 같은 Metadata 노출
- 안전하게 처리되지 않은 Application 재시도
공격자도 자신이 소유한 Domain에 유효한 Certificate를 발급받을 수 있다.
Certificate 유효
→ 그 Domain과 Public Key의 연결이 검증됨
Certificate 유효
≠ 그 Site가 선하거나 업무상 승인됨
TLS는 보호된 Channel의 속성을 제공하고 Application의 신뢰성과 업무 판단은 별도 계층에서 해결한다.
ㅁ Certificate 오류는 무시해도 될까?
TLS Client가 경고를 내는 이유를 구분해야 한다.
Unknown CA
→ Chain이 신뢰하는 Root로 이어지지 않음
Hostname Mismatch
→ 접속 Hostname이 SAN과 일치하지 않음
Expired / Not Yet Valid
→ Certificate 유효 기간 또는 Client Clock 문제
Incomplete Chain
→ 필요한 Intermediate를 Server가 보내지 않음
Signature / Key Usage 오류
→ Chain이나 Certificate 용도 검증 실패
개발 환경의 Self-Signed Certificate라고 Browser 경고를 습관적으로 무시하면 실제 Man-in-the-Middle과 단순한 Local 설정을 구분하는 검증 절차가 사라진다.
개발용 CA나 Certificate를 해당 환경의 Trust Store에 명시적으로 제한해 설치하고 Production Trust와 분리한다.
--insecure 같은 검증 비활성화 Option은 암호화된 상대가 누구인지 확인하지 않게 만들므로 정상 운영 해결책이 아니다.
ㅁ Private Key가 유출되면 무엇을 해야 할까?
Certificate 파일은 배포할 수 있지만 Private Key는 Server Identity 증명의 핵심 비밀이다.
Private Key가 유출되면 공격자가 해당 Key로 Server를 사칭할 수 있는 위험이 생긴다.
대응에는 다음이 포함될 수 있다.
- 노출된 Key의 사용을 즉시 중단한다.
- 새 Private Key를 안전한 환경에서 생성한다.
- 새 Certificate를 발급하고 배포한다.
- 기존 Certificate의 폐기 절차를 수행한다.
- Key가 사용된 시스템과 Log를 조사한다.
- 자동화 Token과 Backup 등 추가 유출 경로를 제거한다.
파일 권한만으로 끝나는 문제가 아니다.
HSM이나 Key Management System, 접근 통제, Rotation과 Audit가 운영 설계에 포함될 수 있다.
Forward Secrecy는 과거 Session 일부를 보호하는 데 도움이 되지만 유출된 장기 Key로 앞으로의 사칭을 막아 주지는 않는다.
ㅁ TLS 실패를 어느 단계에서 나눠 볼까?
“HTTPS가 안 된다”는 한 문장에는 여러 단계가 숨어 있다.
1. DNS Resolution
2. Route와 ARP/NDP
3. TCP 또는 QUIC 연결
4. TLS Version과 Cipher 협상
5. Certificate Chain과 Hostname 검증
6. Finished 검증과 Key 확정
7. HTTP Request 전송
8. Application Response
증상도 다를 수 있다.
TCP SYN Timeout
→ TLS ClientHello도 보내지 못함
TLS Alert: unknown_ca
→ TCP는 연결됐지만 Trust 검증 실패
TLS Alert: handshake_failure
→ Version, Algorithm, Client 인증 등 협상 문제 가능
HTTP 403
→ TLS는 성공했고 Application이 요청을 거절
하나의 Error String만으로 원인을 확정하지 말고 Packet Capture, TLS Client 출력, Server Log와 Certificate 내용을 함께 본다.
ㅁ 우편 비유를 네트워크 용어로 바꾸면
비유를 실제 TLS Message와 검증으로 다시 연결해 보자.
| 우편 이야기 | 실제 용어 | 정확한 의미 |
| 사용할 자물쇠 방식과 재료를 제안 | ClientHello | Version, Cipher, Key Share, SNI, ALPN 등을 제시 |
| 사용할 방식과 Server 재료를 선택 | ServerHello | TLS Version, Cipher Suite와 Server Key Share 선택 |
| 공인된 신분증과 Public Key | Certificate Chain | Host Identity를 Public Key와 신뢰 체인에 연결 |
| 신분증 이름과 방문 주소 비교 | Hostname Verification | 접속 Hostname을 SAN과 검증 |
| 신분증 주인의 비밀 서명 | CertificateVerify | Server가 Certificate Private Key를 소유했음을 Transcript 서명으로 증명 |
| 인사 내용 전체의 봉인 확인 | Finished | Handshake Secret과 Transcript의 일치 및 무결성 확인 |
| 양쪽만 계산한 일회용 자물쇠 재료 | Ephemeral Key Exchange | Shared Secret을 Network로 직접 보내지 않고 합의 |
| 이후 편지의 잠금과 변조 봉인 | AEAD Record Protection | Application Data의 Confidentiality와 Integrity 제공 |
| 미리 신뢰하도록 설치한 발급 기관 | Trust Store / Root CA | Certificate Chain 검증의 Local Trust Anchor |
| 양쪽 모두 신분증 제시 | mTLS | Server와 Client Certificate Authentication 수행 |
| 이전 접수표로 빠르게 재연결 | Session Resumption | PSK와 Ticket으로 Handshake 비용을 줄임 |
| 확인 전 먼저 보내는 재배송 가능 편지 | 0-RTT Early Data | 지연을 줄이지만 Replay 위험을 고려해야 함 |
TLS 1.3의 핵심 증명을 모으면 다음과 같다.
Certificate Chain + Hostname
→ 이 Public Key가 접속하려던 이름과 신뢰 체인에 연결되는가?
CertificateVerify
→ 상대가 그 Public Key의 Private Key를 실제로 소유하는가?
Ephemeral Key Exchange
→ 양쪽이 연결별 Shared Secret을 만들었는가?
Finished
→ Handshake Transcript와 파생 Secret이 양쪽에서 일치하는가?
AEAD
→ 이후 Record가 읽히거나 바뀌지 않도록 보호되는가?
ㅁ Packet Capture에서는 무엇이 보일까?
TLS 1.3 연결을 암호 해제 Key 없이 Capture하면 일반적으로 다음 흐름을 볼 수 있다.
TCP 3-Way Handshake
ClientHello
ServerHello
암호화된 Handshake와 Application Data
TCP 연결 종료
TLS 1.3에서는 ServerHello 뒤의 Certificate와 CertificateVerify도 암호화되므로
Passive Capture만으로 Certificate 내용을 읽을 수 없을 수 있다.
Wireshark Display Filter 예시는 다음과 같다.
tls || tcp.port == 443
확인할 항목은 다음과 같다.
- ClientHello가 실제로 전송되었는가?
- ServerHello 또는 TLS Alert가 돌아왔는가?
- 선택한 TLS Version은 무엇인가?
- TCP Retransmission 때문에 Handshake가 지연되는가?
- TLS 성공 뒤 Application Data Record가 이어지는가?
TLS Key Log를 Client가 명시적으로 제공하면 Wireshark가 Lab Traffic을 복호화할 수 있는 환경도 있다.
Session Key Log는 보호된 내용을 평문으로 드러낼 수 있는 민감 정보이므로 Production에서 수집하거나 공유하면 안 된다.
ㅁ OpenSSL로 Certificate 내용을 확인해 보기
Certificate 파일이 있다면 다음처럼 주요 Field를 확인할 수 있다.
openssl x509 -in server.crt -noout \
-subject -issuer -serial -dates \
-ext subjectAltName \
-fingerprint -sha256
출력에서 다음 질문을 확인한다.
- Subject와 Issuer가 누구인가?
- SAN에 접속할 DNS Name이나 IP가 있는가?
- Not Before와 Not After 사이에 현재 시간이 있는가?
- SHA-256 Fingerprint가 배포 문서와 일치하는가?
Fingerprint 비교는 신뢰할 수 있는 별도 Channel에서 기대값을 얻었을 때 의미가 있다.
공격자가 Certificate와 Fingerprint를 같은 Channel에서 함께 바꾸면 독립적인 검증이 아니다.
ㅁ Local TLS Server를 직접 만들어 보기
다음 실습은 개인 Computer의 Loopback에서만 수행한다.
openssl, curl, tcpdump가 필요하며 외부 Server나 저장소를 사용하지 않는다.
먼저 실습 Directory를 만들고 Private Key 파일의 기본 권한을 제한한다.
mkdir tls-local-lab
cd tls-local-lab
umask 077
하루 동안만 사용할 Self-Signed Certificate와 Private Key를 만든다.
openssl req -x509 -newkey rsa:2048 -sha256 -nodes \
-keyout server.key \
-out server.crt \
-days 1 \
-subj '/CN=localhost' \
-addext 'subjectAltName=DNS:localhost,IP:127.0.0.1' \
-addext 'basicConstraints=critical,CA:TRUE'
이 실습에서는 하나의 Self-Signed Certificate를 Server Certificate이자 Local Trust Anchor로 명시적으로 사용한다.
실제 Public Web PKI의 Root–Intermediate–Leaf 구성을 재현한 것은 아니다.
-nodes는 Private Key를 Password로 암호화하지 않는다는 뜻이다.
자동 실행을 단순화한 Local Lab 설정이며 파일을 외부에 공유하거나 운영 Server에 그대로 적용하지 않는다.
Certificate Field를 확인한다.
openssl x509 -in server.crt -noout \
-subject -issuer -dates \
-ext subjectAltName \
-fingerprint -sha256
첫 번째 Terminal에서 TLS Server를 실행한다.
openssl s_server \
-accept 8443 \
-cert server.crt \
-key server.key \
-www
두 번째 Terminal에서 Certificate를 Local Trust Anchor로 지정하고 Hostname을 검증한다.
openssl s_client \
-connect 127.0.0.1:8443 \
-servername localhost \
-CAfile server.crt \
-verify_hostname localhost \
-verify_return_error
출력에서 다음을 확인한다.
Protocol
Cipher
Peer Certificate
Verification: OK 또는 Verify return code: 0
OpenSSL Version에 따라 출력 표현은 달라질 수 있다.
curl로 HTTPS 요청을 보낸다.
curl --cacert server.crt https://localhost:8443/
--cacert는 이 명령에서 신뢰할 Certificate를 명시한다. 검증을 끄는 -k와 다르다.
ㅁ Hostname 검증 실패를 비교해 보기
같은 Certificate를 신뢰하더라도 접속하려는 이름이 SAN과 다르면 검증이 실패해야 한다.
openssl s_client \
-connect 127.0.0.1:8443 \
-servername wrong.example \
-CAfile server.crt \
-verify_hostname wrong.example \
-verify_return_error
이 명령은 실패를 관찰하기 위한 것이다.
Trust Anchor 일치
+ Certificate Signature 검증 가능
+ Hostname 불일치
= 전체 Identity 검증 실패
“Certificate를 신뢰한다”와 “이 Certificate가 지금 접속한 이름에 유효하다”는 별도 조건임을 확인한다.
ㅁ TLS Packet을 Capture해 보기
Server를 실행한 상태에서 세 번째 Terminal로 Loopback Packet을 저장한다.
Linux에서는 보통 lo, macOS에서는 보통 lo0를 사용한다.
sudo tcpdump -n -i lo -s 0 -w tls-local.pcap \
'tcp port 8443'
그 뒤 curl이나 openssl s_client를 다시 실행한다.
Wireshark에서 다음 순서를 확인한다.
TCP SYN, SYN-ACK, ACK
TLS ClientHello
TLS ServerHello
암호화된 TLS Record
TCP FIN 또는 RST
Capture에 HTTP 응답 본문이 평문으로 보이지 않는지 확인한다.
ClientHello의 SNI, ALPN Offer와 ServerHello 뒤 암호화 범위도 비교한다.
OpenSSL과 curl의 Option, TLS Version에 따라 실제 Extension은 달라질 수 있다.
실습이 끝나면 Server를 종료하고 Private Key를 포함한 파일을 안전하게 삭제한다.
rm -f server.key server.crt tls-local.pcap
cd ..
rmdir tls-local-lab
실제 업무 Certificate와 Private Key를 실습 파일처럼 삭제하거나 공유하지 않는다.
ㅁ 핵심 정리
- TLS는 도청, 변조, 사칭에 대응하기 위해 Confidentiality, Integrity, Authentication을 결합한다.
- TCP Handshake, TLS Handshake와 HTTP 처리는 서로 다른 단계다.
- TLS는 Public Key Cryptography로 Identity와 Key Agreement를 연결하고 대칭키로 대량의 Record를 보호한다.
- ClientHello는 Version, Cipher, Key Share, SNI와 ALPN 후보를 제시하고 ServerHello는 사용할 조건과 Server Key Share를 선택한다.
- TLS 1.3에서는 ServerHello 뒤의 Handshake Message가 암호화되므로 Passive Capture에 Certificate가 보이지 않을 수 있다.
- Certificate Chain은 Public Key와 Host Identity를 Trust Store의 Root에 연결한다.
- Client는 Chain Signature뿐 아니라 SAN Hostname, 유효 기간, Key Usage와 Trust Policy를 함께 검증한다.
- CertificateVerify는 Server가 Certificate Public Key에 대응하는 Private Key를 소유했음을 Handshake Transcript 서명으로 증명한다.
- Finished는 양쪽의 Handshake Secret과 Transcript가 일치하는지 확인한다.
- Ephemeral Key Exchange와 안전한 Secret 폐기는 장기 Private Key 유출이 과거 Session에 미치는 영향을 줄이는 Forward Secrecy를 제공한다.
- AEAD는 TLS Record의 Encryption과 암호학적 Integrity를 함께 제공한다.
- TLS Record, TCP Segment와 HTTP Message의 Boundary는 서로 같지 않다.
- mTLS의 Client Authentication과 Application Authorization은 별도 단계다.
- Session Resumption은 연결 비용을 줄이지만 0-RTT Early Data는 Replay 위험 때문에 요청 의미를 고려해야 한다.
- TLS는 IP·Port·Traffic 크기와 시간까지 모두 숨기지 않으며 Endpoint 침해와 Application 취약점을 해결하지 않는다.
- Self-Signed Certificate 경고를 무시하기보다 제한된 Trust Anchor와 Hostname 검증을 명시적으로 구성해야 한다.
핵심 용어 : Transport Layer Security, TLS 1.3, HTTPS, Confidentiality, Integrity, Authentication, ClientHello, ServerHello, EncryptedExtensions, SNI, ALPN, Cipher Suite, Key Share, Ephemeral Diffie-Hellman, Certificate, X.509, Certificate Chain, Root CA, Intermediate CA, Leaf Certificate, Trust Store, Subject Alternative Name, Hostname Verification, CertificateVerify, Finished, Handshake Transcript, Traffic Key, AEAD, Forward Secrecy, mTLS, Session Resumption, PSK, 0-RTT, Replay, TLS Termination
TLS는 우체부를 믿게 만드는 기술이 아니다. 우체부가 편지를 운반할 수는 있어도 내용을 읽거나 몰래 바꾸기 어렵게 만들고, 받은 편지가 신뢰한 상대와 합의한 Key로 보호되었는지 Endpoint가 직접 검증하게 한다.
ㅁ 다음 이야기
지금까지 편지는 주소를 찾고, 경로를 지나고, 크기를 조절하고, 허용된 문을 통과해 암호화된 Channel 안에서 전달되었다.
이제 실제 장애를 만났다고 하자. 가장 먼저 실행하기 쉬운 명령은 ping이다.
하지만 Echo Reply가 오지 않는다는 사실만으로 Server가 꺼졌다고 말할 수 있을까? 반대로 ping이 성공하면 HTTPS와 Application도 정상이라고 말할 수 있을까?
다음 글에서는 「ping이 실패하면 정말 서버가 죽은 것일까?」라는 질문을 통해 ICMP Echo, RTT, Packet Loss, Firewall Policy와 Layer별 진단 범위를 살펴본다.
'DevOps > Network' 카테고리의 다른 글
| [Network] 25. traceroute는 보이지 않는 길을 어떻게 보여줄까? (0) | 2026.07.27 |
|---|---|
| [Network] 24. ping이 실패하면 정말 서버가 죽은 것일까? (1) | 2026.07.26 |
| [Network] 22. 방화벽은 편지의 무엇을 보고 문을 열어줄까? (0) | 2026.07.24 |
| [Network] 21. 여러 집이 하나의 공인 주소를 함께 쓸 수 있을까? (0) | 2026.07.23 |
| [Network] 20. 가장 좁은 길의 크기를 출발지에서 어떻게 알 수 있을까? (0) | 2026.07.22 |
