HTTPS 주소의 자물쇠는 “data가 암호화된다”는 한 문장보다 많은 일을 한다. 일반적인 TLS 1.3 연결은 server identity를 확인하고, client와 server만 계산할 수 있는 shared secret을 만든 뒤, 그 값에서 실제 traffic을 보호할 대칭 key를 파생한다.
여기서 자주 생기는 오해가 있다.
- server가 public key로 session key를 암호화해 보내는 것이 TLS 1.3의 일반적인 흐름은 아니다.
- certificate는 data 자체를 암호화하는 열쇠가 아니라 identity와 public key를 묶고 서명을 검증하는 데 쓰인다.
- private key는 network로 전송되지 않는다.
TLS 1.3 handshake를 message 순서대로 보면 이 관계가 분명해진다.
현재 사용해야 할 TLS version
SSL 2.0과 3.0은 오래전에 금지됐다. TLS는 SSL의 단순한 이름 변경이라기보다 그 뒤를 잇는 별도 protocol version 계열이다.
IETF는 RFC 8996에서 TLS 1.0과 1.1을 공식적으로 deprecated했다. 현재 배포는 TLS 1.3을 우선하고, 호환성이 필요한 경우 최신 보안 지침에 맞춘 TLS 1.2를 함께 운영하는 것이 일반적이다.
“TLS 1.2 이상이면 어떤 cipher든 안전하다”는 뜻은 아니다. protocol version, cipher suite, certificate validation, library patch와 application configuration을 함께 관리해야 한다.
TLS 1.3 handshake 한눈에 보기
certificate 기반의 일반적인 TLS 1.3 연결을 단순화하면 다음과 같다.
Client Server
| |
| ClientHello |
| - supported_versions |
| - cipher suites |
| - key_share |
| ---------------------------------------------------> |
| |
| ServerHello |
| - selected version·cipher suite |
| - server key_share |
| <--------------------------------------------------- |
| |
| 양쪽이 (EC)DHE shared secret과 |
| handshake traffic key를 각각 계산 |
| |
| EncryptedExtensions |
| Certificate |
| CertificateVerify |
| Finished |
| <--------------------------------------------------- |
| |
| Finished |
| ---------------------------------------------------> |
| |
| <========== encrypted application data ==========> |
PSK-only handshake나 client certificate를 쓰는 경우에는 message 구성이 달라질 수 있다. 위 흐름은 웹에서 흔한 certificate 기반 server authentication을 설명한 것이다.
1. ClientHello: 가능한 조건과 key share를 보낸다
client는 ClientHello에 다음 정보를 담는다.
- 지원하는 TLS version
- 사용할 수 있는 TLS 1.3 cipher suite
- key exchange group과 client의 ephemeral public key share
- SNI server name과 ALPN 같은 extension
- random과 session resumption 정보
TLS 1.3 cipher suite는 주로 AEAD algorithm과 hash를 정한다. key exchange와 signature algorithm은 별도 extension으로 협상된다. TLS 1.2의 cipher suite 이름을 그대로 해석하면 안 되는 지점이다.
2. ServerHello: version과 key share를 확정한다
server는 선택한 version, cipher suite와 자신의 ephemeral key share를 ServerHello로 보낸다.
일반적인 ECDHE handshake에서 양쪽은 상대의 public key share와 자신의 ephemeral private key로 같은 shared secret을 계산한다. private key나 완성된 session key를 상대에게 보내지 않는다.
client private + server public ─┐
├─ 같은 ECDHE shared secret
server private + client public ─┘
ephemeral key를 쓰고 handshake가 끝난 뒤 폐기하면 장기 server private key가 훗날 노출되더라도 과거 session을 바로 복호화하기 어려운 forward secrecy를 얻는다.
3. HKDF: 용도별 traffic key를 파생한다
ECDHE 결과는 application data를 곧바로 암호화하는 단일 key가 아니다. TLS 1.3은 HKDF와 handshake transcript를 이용해 단계·방향·용도별 secret을 분리한다.
- client handshake traffic secret
- server handshake traffic secret
- client application traffic secret
- server application traffic secret
client→server와 server→client key도 구분된다. 이런 key separation 덕분에 하나의 값을 여러 용도로 재사용하지 않는다.
4. Certificate와 CertificateVerify: 누구와 연결했는지 확인한다
server certificate는 service identity와 public key를 묶는다. client는 보통 다음을 확인한다.
- certificate chain이 신뢰하는 root까지 검증되는가?
- 현재 시각에 certificate가 유효한가?
- 접속한 hostname이
subjectAltName의 identity와 일치하는가? - 용도와 policy가 이 server authentication에 맞는가?
RFC 9525는 service identity를 Common Name 문자열이 아니라 적절한 subjectAltName identifier로 확인하도록 정리한다.
CertificateVerify에서는 server가 certificate에 대응하는 private key로 현재 handshake transcript에 서명한다. client는 certificate의 public key로 서명을 검증한다. 이 과정이 “certificate를 제시한 상대가 private key도 실제로 갖고 있는가”를 확인한다.
certificate chain이 유효해도 hostname을 확인하지 않으면 다른 service용 certificate를 잘못 신뢰할 수 있다. 반대로 hostname만 같고 신뢰 chain을 검증하지 않아도 충분하지 않다.
5. Finished: handshake가 변조되지 않았는지 확인한다
양쪽은 지금까지의 handshake message transcript와 파생 secret으로 Finished 값을 계산한다. 상대의 값이 일치해야 negotiation과 certificate exchange가 중간에서 바뀌지 않았음을 확인하고 application data 단계로 넘어간다.
TLS 1.3 record protection은 AEAD를 사용한다. encryption과 integrity를 하나의 authenticated encryption 연산으로 제공한다.
0-RTT가 항상 이득은 아니다
session resumption에서는 client가 handshake 완료 전에 early data를 보내는 0-RTT를 사용할 수 있다. latency는 줄지만 TLS 1.3 RFC는 0-RTT data에 일반적인 replay 방지가 없다고 경고한다.
결제, 주문 생성, 상태 변경처럼 재실행에 민감한 request에는 그대로 허용하면 안 된다. server가 idempotency와 anti-replay policy를 별도로 설계하거나 0-RTT를 끄는 편이 안전하다.
command line에서 확인하기
OpenSSL로 TLS 1.3 연결, SNI와 hostname verification을 함께 확인할 수 있다.
openssl s_client \
-connect example.com:443 \
-servername example.com \
-tls1_3 \
-verify_hostname example.com \
-verify_return_error \
</dev/null
s_client는 test tool이라 default behavior만 믿고 certificate error를 놓치면 안 된다. -verify_hostname과 -verify_return_error를 명시했다.
curl에서는 TLS 1.2 이상, 최대 TLS 1.3 범위로 실제 HTTP response header를 확인할 수 있다.
curl -Iv \
--tlsv1.2 \
--tls-max 1.3 \
https://example.com/
certificate 검증을 끄는 -k 또는 --insecure를 운영 점검 절차로 사용하면 identity verification이 사라진다. 원인을 해결하지 않은 채 성공처럼 보이게 만들 뿐이다.
TLS가 보호하지 않는 경계
TLS는 client와 TLS endpoint 사이의 transport를 보호한다. 다음까지 자동으로 해결하지는 않는다.
- client나 server process 자체가 침해된 경우
- TLS termination 이후 내부 hop이 평문인 경우
- application의 authorization·XSS·SQL injection
- log와 database에 저장된 data
- 사용자가 phishing domain에 접속한 경우
- mail relay 사이에서 opportunistic STARTTLS만 사용한 경우의 end-to-end confidentiality
reverse proxy나 load balancer에서 TLS를 종료한다면 그 뒤 hop의 trust boundary와 재암호화 여부를 따로 설계해야 한다.
Istio에서 mTLS를 구성하는 방법은 server만 인증하는 일반 HTTPS와 양방향 authentication의 차이를 보는 데 도움이 된다. cipher와 signature의 기초는 Security and Cryptography 정리로 이어서 볼 수 있다.
참고 자료
'배움과 성장 > 네트워크·보안' 카테고리의 다른 글
| JWT와 HMAC 검증: HS256 키·alg·claim을 안전하게 다루는 법 (0) | 2024.08.26 |
|---|---|
| XSS와 CSRF 차이: 출력 인코딩·CSRF 토큰·SameSite 쿠키 (0) | 2024.08.18 |
| 해저 인터넷 케이블 설치·매설·수리 과정: 왜 모든 구간을 묻지 않을까 (0) | 2024.08.17 |
| 진폭·주파수·위상 차이: 사인파·dB·시간 지연으로 이해하기 (0) | 2024.08.17 |
| 반송파란 무엇인가: 변조·복조와 디지털 통신까지 (0) | 2024.08.17 |
댓글