LLM WikiAccess-protected knowledge portal

WIKI

TLS/HTTPS 동작 원리와 인증서 관리

왜 TLS를 이해해야 하는가 DB 서버에 ssl required 를 설정했는데 애플리케이션 연결이 갑자기 끊겼다. 인증서 만료가 원인이었지만, 에러 메시지는 SSL connection error 뿐이고 어떤 인증서가 문제인지, 왜 갱신이 안 됐는지 모르는 채로 롤백하기 쉽다. TLS는 네트워크 계층에서 암호화 Confidentiality , 인증 Authentication , 무결성 Integrity 세 가지를 동시에 제공한다

경로human/study/content/networking-fundamentals/02-tls-https-certificates.md
카테고리Study
태그#certificates #fundamentals #https #infra #networking #study #tls

왜 TLS를 이해해야 하는가

DB 서버에 ssl: required를 설정했는데 애플리케이션 연결이 갑자기 끊겼다. 인증서 만료가 원인이었지만, 에러 메시지는 SSL connection error뿐이고 어떤 인증서가 문제인지, 왜 갱신이 안 됐는지 모르는 채로 롤백하기 쉽다.

TLS는 네트워크 계층에서 암호화(Confidentiality), 인증(Authentication), 무결성(Integrity) 세 가지를 동시에 제공한다. DB 운영자라면 TLS 핸드셰이크가 어떻게 동작하고, 인증서 체인이 어떻게 검증되며, 인증서 만료가 어떤 식으로 장애를 일으키는지 이해해야 한다. 그래야 openssl s_client 한 줄로 문제를 진단할 수 있다.


TLS의 세 가지 역할

역할설명없으면 생기는 문제
기밀성데이터를 암호화해 도청 방지네트워크 스니핑으로 SQL·결과 노출
인증서버(또는 클라이언트)가 주장하는 신원을 검증MITM 공격 — 가짜 DB 서버로 연결될 수 있음
무결성MAC(Message Authentication Code)으로 데이터 변조 감지패킷 위·변조 후 삽입 공격

TLS 버전 현황

TLS 1.3이 2018년 8월 RFC 8446으로 표준화됐고, 2026년 기준으로 HTTPS 트래픽의 약 95%가 TLS 1.3으로 처리된다. TLS 1.0/1.1은 RFC 8996(2021)으로 폐기됐다.

버전RTT상태
SSL 2.0 / SSL 3.0폐기 (POODLE, DROWN 취약점)
TLS 1.0 / 1.12-RTTRFC 8996으로 폐기
TLS 1.22-RTT여전히 사용 중이나 권장하지 않음
TLS 1.31-RTT (0-RTT 지원)현재 권장 표준

TLS 1.3 핸드셰이크

TLS 1.3의 가장 중요한 개선은 핸드셰이크를 1-RTT로 단축한 것이다. TLS 1.2에서는 암호 협상(Cipher Suite)을 먼저 하고 키 교환을 나중에 했지만, TLS 1.3에서는 클라이언트가 첫 메시지에 키 공유(Key Share)까지 같이 보낸다.

클라이언트 서버 ── RTT 1 ── ClientHello 지원 TLS 버전, Cipher Suites, 랜덤값, Key Share (ECDH 공개키) ServerHello + EncryptedExtensions + Certificate + CertificateVerify + Finished Key Share 선택 → 공유 비밀 계산 → 서버 인증서 전송 (이미 암호화) → Finished 서버측 핸드셰이크 완료 (암호화 통신 시작 가능) Finished (클라이언트) ✅ 1-RTT 완료 → 양방향 암호화 통신 SQL 쿼리 (암호화된 애플리케이션 데이터) 쿼리 결과 (암호화된 응답) 0-RTT (Early Data): 재연결 시 사용 가능 이전 세션의 PSK(Pre-Shared Key)를 사용해 첫 패킷부터 데이터를 보낼 수 있음. ⚠ Replay 공격에 취약 — DB 연결에서는 일반적으로 비활성화. GET처럼 멱등성이 보장되는 경우에만 사용.
TLS 1.3 핸드셰이크 (1-RTT)

TLS 1.2와의 비교

TLS 1.2는 핸드셰이크에 2-RTT가 필요했다. ClientHello → ServerHello → (1 RTT) → ClientKeyExchange → ChangeCipherSpec → (1 RTT) → 데이터. 지연이 두 배다. WAN 환경에서 RTT가 100ms이면 연결 수립에만 200ms가 걸렸다. TLS 1.3에서 이 비용이 절반으로 줄었다.


인증서 체인

TLS 인증서는 계층 구조로 서로를 서명해(chain of trust) 신뢰를 구축한다.

🔐 Root CA
DigiCert / Let's Encrypt ISRG Root X1
브라우저·OS Trust Store에 내장
📋 Intermediate CA
Let's Encrypt E5 / R10
서버가 핸드셰이크 중 전송
🌐 Leaf (Server) Certificate
db.prod.example.com
유효기간 90일 (Let's Encrypt) / 1년
클라이언트 검증 순서
① Leaf 인증서 서명 → Intermediate CA로 검증 ② Intermediate CA 서명 → Root CA로 검증 ③ Root CA → Trust Store 내 자체 서명 확인 ④ 유효기간·도메인(CN/SAN) 확인 ⑤ 폐기 여부 확인 (CRL / OCSP)
인증서 체인 구조

핸드셰이크 중 서버는 Leaf 인증서와 Intermediate CA 인증서를 함께 전송한다. Root CA는 전송하지 않는다. 클라이언트의 Trust Store에 이미 있어야 하기 때문이다. 브라우저와 OS는 약 150개의 Root CA를 내장하고 있다.

인증서 필드 핵심

Subject: CN=db.prod.example.com
Subject Alternative Names (SAN): db.prod.example.com, db-ro.prod.example.com
Issuer: Let's Encrypt E5
Validity: 2026-05-01 ~ 2026-07-30 (90일)
Public Key: EC 256-bit (P-256)
Key Usage: Digital Signature, Key Encipherment
Extended Key Usage: TLS Web Server Authentication

SAN(Subject Alternative Names)이 중요하다. 다중 호스트명을 하나의 인증서로 커버할 수 있다. 와일드카드(*.prod.example.com)보다 SAN 목록을 명시하는 것이 보안상 낫다.


인증서 폐기 확인

인증서가 만료 전에 폐기되는 경우(개인키 유출, 설정 오류 등)를 감지해야 한다.

CRL (Certificate Revocation List)

CA가 폐기된 인증서 목록을 주기적으로 게시한다. 클라이언트가 CRL 파일을 다운로드해서 확인한다. 파일 크기가 크고(수 MB), 업데이트 주기가 길다(수 시간~수 일). 실시간 폐기 감지에 취약하다.

OCSP (Online Certificate Status Protocol)

CA의 OCSP 서버에 인증서 일련번호를 조회해 현재 상태를 실시간으로 확인한다. 빠르지만 CA OCSP 서버가 다운되면 연결이 지연되거나 실패한다. 또한 클라이언트 IP가 CA에 노출된다는 프라이버시 문제가 있다.

Let's Encrypt는 2025년 OCSP 지원을 종료했다. 단기 인증서(90일)로 폐기 필요성 자체를 줄이는 방향으로 전환했다.

OCSP Stapling

서버가 미리 OCSP 응답을 가져와 캐시해두고, 핸드셰이크 중 클라이언트에게 직접 전달한다. CA OCSP 서버에 클라이언트가 직접 연결할 필요가 없어 지연과 프라이버시 문제가 해소된다.

# nginx에서 OCSP Stapling 활성화
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/chain.pem;
resolver 8.8.8.8 1.1.1.1 valid=300s;

mTLS (Mutual TLS)

일반 TLS는 클라이언트가 서버 인증서를 검증한다. mTLS는 양방향이다 — 서버도 클라이언트 인증서를 검증한다.

단방향 TLS (일반적인 HTTPS)
클라이언트
→ 서버 인증서 검증 암호화 통신
서버
인증서 제공
클라이언트 인증 없음 — 누구나 연결 가능
mTLS (상호 인증)
클라이언트
클라이언트 인증서 제공
→ 서버 인증서 검증 ← 클라이언트 인증서 검증
서버
서버 인증서 제공
인증서가 있는 클라이언트만 연결 가능 — 서비스 메시, 내부 API
mTLS vs 단방향 TLS

mTLS는 쿠버네티스 서비스 메시(Istio, Linkerd)에서 기본 제공된다. 모든 pod 간 트래픽이 자동으로 mTLS로 암호화·인증된다. DB 접근에서도 mTLS를 적용하면 암호 없이 인증서 기반으로 접근 제어가 가능하다.


DB 운영에서의 TLS 설정

MySQL TLS

# my.cnf (서버)
[mysqld]
require_secure_transport = ON
ssl_ca   = /etc/mysql/ca.pem
ssl_cert = /etc/mysql/server-cert.pem
ssl_key  = /etc/mysql/server-key.pem
tls_version = TLSv1.2,TLSv1.3

# 클라이언트 연결 확인
mysql -h db.prod -u user --ssl-ca=ca.pem --ssl-mode=VERIFY_IDENTITY -p
# 현재 세션 TLS 정보 확인
SHOW STATUS LIKE 'Ssl_%';
-- Ssl_version: TLSv1.3
-- Ssl_cipher: TLS_AES_256_GCM_SHA384

# 인증서 유효기간 확인
SHOW STATUS LIKE 'Ssl_server_not_after';

PostgreSQL TLS

# postgresql.conf
ssl = on
ssl_ca_file   = 'root.crt'
ssl_cert_file = 'server.crt'
ssl_key_file  = 'server.key'
ssl_min_protocol_version = 'TLSv1.2'

# pg_hba.conf — clientcert=verify-full: mTLS
hostssl all all 10.0.0.0/8 scram-sha-256 clientcert=verify-full

인증서 관리 자동화

수동 갱신은 실수를 부른다. Let's Encrypt 90일 인증서가 표준이 되면서 자동화는 선택이 아닌 필수다.

Let's Encrypt
(ACME 프로토콜)
cert-manager
(Kubernetes)
또는 certbot (VM)
Kubernetes Secret
또는 로컬 파일
DB / Proxy / LB
자동 재로드
자동화 핵심 포인트
만료 30일 전 자동 갱신 트리거 ACME DNS-01 챌린지 (내부 도메인 지원) 갱신 성공/실패 알림 설정 갱신 후 DB 프로세스 무중단 재로드 (SIGHUP) 인증서 모니터링: Prometheus ssl_certificate exporter
인증서 생명주기 자동화

실용 갱신 체크리스트

# 인증서 만료일 확인 (openssl)
openssl x509 -in /etc/ssl/server.crt -noout -dates

# 원격 서버 인증서 확인
openssl s_client -connect db.prod.example.com:3306 -starttls mysql 2>/dev/null \
  | openssl x509 -noout -dates

# 인증서 체인 전체 확인
openssl s_client -connect db.prod.example.com:5432 -showcerts 2>/dev/null \
  | grep -E "subject|issuer|notAfter"

TLS 세션 재개

커넥션 풀을 사용하더라도 풀이 재시작되거나 새 연결을 만들 때마다 TLS 핸드셰이크 비용이 든다. 세션 재개로 이 비용을 줄일 수 있다.

Session Resumption (TLS 1.2): 서버가 Session ID를 발급. 클라이언트가 같은 ID로 재연결하면 전체 핸드셰이크를 생략한다. 서버 메모리에 세션 상태를 저장해야 해서 로드밸런서 환경에서 복잡하다.

Session Tickets (TLS 1.2/1.3): 서버가 세션 상태를 암호화해 클라이언트에게 전달. 클라이언트가 재연결 시 티켓을 보내면 서버가 복호화해 상태를 복원. 서버 측 상태 저장이 불필요해 로드밸런서 환경에 적합.

PSK (Pre-Shared Key, TLS 1.3): TLS 1.3의 세션 재개 방식. 0-RTT도 가능하지만 DB 연결에서는 일반적으로 1-RTT PSK만 사용.

MySQL 8.0+와 PostgreSQL 15+는 TLS 세션 재개를 지원한다. 커넥션 풀 재시작 시 첫 연결에만 전체 핸드셰이크가 발생한다.


실무 트러블슈팅

흔한 TLS 오류

오류원인확인 방법
certificate has expired인증서 만료openssl x509 -dates
certificate verify failed체인 불완전 / CA 미신뢰openssl verify -CAfile ca.pem server.crt
hostname mismatchCN/SAN에 연결 도메인 없음openssl x509 -text → SAN 확인
TLS version mismatch클라이언트/서버 최소 TLS 버전 불일치openssl s_client -tls1_2 ...
handshake timeout네트워크 방화벽 or OCSP 서버 응답 대기tcpdump로 패킷 흐름 확인

진단 명령

# TLS 핸드셰이크 전체 과정 출력
openssl s_client -connect db.prod.example.com:5432 -starttls postgres -state -debug 2>&1 | head -60

# 특정 TLS 버전만 테스트
openssl s_client -connect host:3306 -starttls mysql -tls1_3

# 인증서 체인 유효성 검증
openssl verify -CAfile /etc/ssl/ca-bundle.crt /etc/ssl/server.crt

# curl로 TLS 정보 포함 응답
curl -v --cacert ca.pem https://db-proxy.prod.example.com/health 2>&1 | grep -E "SSL|TLS|cert"

핵심 요약


References