TLS/SSL 완전 가이드: HTTPS를 만드는 암호화 프로토콜의 모든 것
자물쇠 하나가 인터넷 상거래를 가능하게 만들었다
1990년대 중반, 인터넷이 대중화되기 시작했을 때 심각한 문제가 있었습니다. 모든 통신이 평문(Plain Text)으로 전송되었기 때문에, 누구나 중간에서 패킷을 가로채 읽을 수 있었습니다.
신용카드 번호를 입력하면? → 해커가 그대로 볼 수 있었습니다.
비밀번호를 전송하면? → 중간에서 탈취 가능했습니다.
개인정보를 보내면? → 모두에게 공개되는 것과 같았습니다.
이런 상황에서 인터넷 쇼핑, 온라인 뱅킹, 이메일이 가능할까요? 불가능했습니다.
그래서 1994년, 넷스케이프(Netscape)는 SSL(Secure Sockets Layer) 을 개발했습니다. 이후 SSL은 TLS(Transport Layer Security) 로 발전했고, 2018년에는 TLS 1.3이 발표되었습니다.
현재 전 세계 웹사이트의 90% 이상이 HTTPS(HTTP + TLS)를 사용합니다. 브라우저 주소창의 작은 자물쇠 아이콘 — 그것이 TLS가 작동하고 있다는 신호입니다.
이 글에서는 TLS/SSL이 무엇인지, 어떻게 작동하는지, TLS 1.3의 개선사항, 그리고 개발자가 HTTPS를 적용하는 방법까지 완전히 정리하겠습니다.
SSL과 TLS — 무엇이 다른가?
SSL의 탄생과 역사
SSL(Secure Sockets Layer) 은 1994년 넷스케이프가 처음 개발했습니다.
- SSL 1.0: 공개되지 않음 (보안 결함으로 폐기)
- SSL 2.0 (1995): 최초 공개 버전, 하지만 곧 취약점 발견
- SSL 3.0 (1996): 대폭 개선되어 널리 사용됨
TLS의 등장
1999년, IETF(Internet Engineering Task Force)가 SSL 3.0을 기반으로 TLS 1.0을 표준화했습니다. SSL에서 TLS로 이름이 바뀐 이유는 표준화 기구가 넷스케이프에서 IETF로 이동했기 때문입니다.
TLS 버전 역사:
| 버전 |
발표 연도 | 주요 특징 | 현재 상태 |
| TLS 1.0 | 1999 | SSL 3.0 기반 표준화 | ❌ 사용 금지 (2020) |
| TLS 1.1 | 2006 | CBC 공격 방어 | ❌ 사용 금지 (2020) |
| TLS 1.2 | 2008 | SHA-256 지원, AEAD 암호 | ✅ 현재 사용 중 |
| TLS 1.3 | 2018 | 1-RTT 핸드셰이크, 취약 암호 제거 | ✅ 권장 |
"SSL/TLS"라고 부르는 이유
기술적으로는 TLS가 정확하지만, SSL이라는 용어가 너무 유명해져서 두 용어를 함께 사용합니다. "SSL 인증서"라고 말하지만 실제로는 TLS를 사용하는 것입니다.
핵심:
SSL은 레거시, TLS가 현대 표준. 하지만 사람들은 여전히 "SSL"이라고 부른다.
TLS/SSL의 3가지 보안 목표
TLS는 인터넷 통신에서 다음 3가지를 보장합니다:
① 기밀성 (Confidentiality) — 암호화
통신 내용을 제3자가 읽을 수 없도록 암호화합니다.
평문: "내 카드번호는 1234-5678-9012-3456"
암호화: "A7B3C9D2E4F1G8H5..."
중간에서 패킷을 가로채도 암호화된 데이터만 보입니다.
② 무결성 (Integrity) — 변조 방지
전송 중 데이터가 변조되지 않았음을 보장합니다.
HMAC(Hash-based Message Authentication Code) 을 사용하여 각 패킷의 해시값을 계산하고, 수신 측에서 재계산하여 비교합니다.
원본 데이터: "송금 1,000원"
해시값: abc123
[중간자가 변조 시도]
변조 데이터: "송금 1,000,000원"
해시값: xyz789 ← 원본과 다름! 거부됨
③ 인증 (Authentication) — 신원 확인
통신 상대방이 진짜 본인인지 확인합니다.
디지털 인증서를 통해 "이 서버가 정말 naver.com인가?"를 검증합니다. 이를 통해 피싱 사이트를 걸러낼 수 있습니다.
TLS의 핵심 원리: 하이브리드 암호화
TLS는 비대칭 암호화와 대칭 암호화를 모두 사용하는 하이브리드 시스템입니다.
비대칭 암호화 (Asymmetric Encryption)
공개 키(Public Key) 와 개인 키(Private Key) 라는 한 쌍의 키를 사용합니다.
특징:
- 공개 키로 암호화 → 개인 키로만 복호화 가능
- 개인 키로 서명 → 공개 키로 검증 가능
- 안전하지만 느림 (RSA, ECDSA 등)
예시:
Alice가 Bob에게 메시지 전송:
1. Alice가 Bob의 공개 키로 메시지 암호화
2. Bob만 자신의 개인 키로 복호화 가능
대칭 암호화 (Symmetric Encryption)
단일 키(Shared Key) 를 양쪽이 공유하여 사용합니다.
특징:
- 같은 키로 암호화 및 복호화
- 빠르지만 키 공유가 위험 (AES, ChaCha20 등)
왜 둘 다 사용하는가?
문제:
- 비대칭 암호화: 안전하지만 너무 느림 (대칭보다 100~1000배 느림)
- 대칭 암호화: 빠르지만 키를 안전하게 공유하기 어려움
해결책 (TLS의 전략):
- 핸드셰이크 단계: 비대칭 암호화로 세션 키(대칭 키)를 안전하게 교환
- 데이터 전송 단계: 세션 키(대칭 암호화)로 빠르게 통신
[비대칭 암호화 - 느리지만 안전]
→ 세션 키 교환
[대칭 암호화 - 빠름]
→ 실제 데이터 전송
TLS 핸드셰이크: 안전한 연결 수립 과정
TLS 핸드셰이크는 클라이언트와 서버가 암호화 통신을 시작하기 전에 세션 키를 교환하는 과정입니다.
TLS 1.2 핸드셰이크 (2-RTT)
RTT(Round-Trip Time): 클라이언트 → 서버 → 클라이언트 한 왕복
Client Server
1. ClientHello -------->
(지원 암호 스위트, 랜덤값)
<-------- 2. ServerHello
(선택된 암호 스위트, 랜덤값)
Certificate
(서버 인증서)
ServerKeyExchange
(키 교환 정보)
ServerHelloDone
3. ClientKeyExchange ---->
(암호화된 Pre-Master Secret)
ChangeCipherSpec
Finished
<-------- 4. ChangeCipherSpec
Finished
[암호화된 데이터 전송 시작]
단계별 설명:
- ClientHello: 클라이언트가 지원하는 암호화 방식 목록 전송
- ServerHello: 서버가 암호화 방식 선택, 인증서 전송
- Key Exchange: Pre-Master Secret 교환, 양측이 Master Secret 생성
- Finished: 핸드셰이크 완료, 세션 키로 통신 시작
총 왕복: 2-RTT (약 100~200ms)
TLS 1.3 핸드셰이크 (1-RTT) — 50% 더 빠름!
TLS 1.3은 핸드셰이크를 1-RTT로 단축했습니다.
Client Server
1. ClientHello -------->
(지원 암호 + 키 공유 정보 미리 전송!)
<-------- 2. ServerHello
(선택 암호 + 키 공유)
Certificate
Finished
[암호화된 애플리케이션 데이터]
3. Finished ----------->
[암호화된 애플리케이션 데이터]
차이점:
| 항목 | TLS 1.2 | TLS 1.3 |
| RTT | 2회 | 1회 |
| 지연 시간 | ~200ms | ~100ms |
| 키 교환 | 2단계 분리 | 1단계 통합 |
핵심 개선:
클라이언트가 첫 번째 메시지에서 키 교환 정보를 미리 포함시켜, 서버가 즉시 세션 키를 생성하고 암호화된 응답을 보낼 수 있습니다.
TLS 1.3의 0-RTT 재개 (0-RTT Resumption)
이전에 연결했던 서버에 재접속할 때, 핸드셰이크 없이 즉시 데이터 전송이 가능합니다!
[첫 방문]
Client → Server: 1-RTT 핸드셰이크
→ 양측이 PSK (Pre-Shared Key) 저장
[재방문]
Client → Server: PSK + 암호화된 HTTP 요청 (0-RTT!)
→ 서버가 즉시 응답
총 지연: HTTP와 동일!
주의: 0-RTT는 재전송 공격(Replay Attack)에 취약할 수 있으므로, 멱등성(Idempotent)이 보장되는 요청(GET 등)에만 사용해야 합니다.
디지털 인증서와 CA (Certificate Authority)
인증서가 필요한 이유
공개 키를 받았을 때, "이 공개 키가 정말 naver.com의 것인가?" 를 어떻게 확인할까요?
문제 시나리오:
사용자 → [해커의 가짜 naver.com] → 해커의 공개 키
사용자는 해커의 공개 키로 비밀번호를 암호화하여 전송 → 해커가 복호화 → 비밀번호 탈취!
해결책: 신뢰할 수 있는 제3자(CA)가 "이 공개 키는 진짜 naver.com의 것입니다" 라고 보증하는 디지털 인증서를 발급합니다.
인증서의 구조
{
"version": "3",
"serialNumber": "0A1B2C3D...",
"issuer": "DigiCert Inc",
"validity": {
"notBefore": "2024-01-01",
"notAfter": "2025-12-31"
},
"subject": "naver.com",
"subjectPublicKeyInfo": {
"algorithm": "RSA",
"publicKey": "MIIBIjANBg..."
},
"signature": "CA의 개인 키로 서명된 값"
}
핵심 필드:
- Issuer: 인증서를 발급한 CA
- Subject: 인증서의 소유자 (도메인)
- Public Key: 서버의 공개 키
- Signature: CA가 개인 키로 서명한 값
CA 신뢰 체인 (Chain of Trust)
브라우저와 운영체제는 루트 CA의 인증서를 미리 저장하고 있습니다.
[루트 CA] (브라우저에 내장)
↓ 서명
[중간 CA]
↓ 서명
[최종 인증서 - naver.com]
검증 과정:
- 서버가 naver.com 인증서 전송
- 브라우저가 중간 CA의 공개 키로 서명 검증
- 중간 CA 인증서를 루트 CA의 공개 키로 검증
- 루트 CA는 브라우저에 내장 → 신뢰 완료!
주요 CA 목록
| CA |
시장 점유율 | 특징 |
| Let's Encrypt | ~40% | 무료, 자동화 (권장!) |
| DigiCert | ~20% | 유료, 엔터프라이즈 |
| Sectigo | ~15% | 중소기업용 |
| GoDaddy | ~10% | 호스팅 통합 |
TLS 1.3의 주요 개선사항
① 취약한 암호 알고리즘 제거
TLS 1.2에서 지원했던 오래되고 위험한 암호화 방식을 완전히 제거했습니다.
제거된 알고리즘:
- RSA 키 교환: 순방향 비밀성(Forward Secrecy) 미제공
- RC4, DES, 3DES: 약한 암호화
- MD5, SHA-1: 충돌 공격 가능한 해시
- 정적 DH: 키 재사용 위험
남은 안전한 알고리즘만:
- 키 교환: Ephemeral Diffie-Hellman (DHE, ECDHE)
- 암호화: AES-GCM, ChaCha20-Poly1305
- 해시: SHA-256, SHA-384
② 순방향 비밀성 (Forward Secrecy)
문제 (TLS 1.2 RSA 방식):
서버의 개인 키가 해킹당하면, 과거의 모든 통신을 복호화할 수 있었습니다.
[해커가 과거 암호화 트래픽 저장]
→ [1년 후 서버 해킹, 개인 키 탈취]
→ [저장된 트래픽 전부 복호화!]
해결 (TLS 1.3 Ephemeral DH):
세션마다 일회용 키를 생성하므로, 서버 키가 유출되어도 과거 세션은 안전합니다.
각 세션 키 = 일회용 임시 키로 생성
→ 서버 개인 키 유출되어도 과거 세션 복호화 불가능
③ 암호화된 SNI (ESNI/ECH)
문제:
TLS 핸드셰이크의 SNI(Server Name Indication) 는 평문으로 전송되어, 어떤 도메인에 접속했는지 ISP가 알 수 있었습니다.
ClientHello:
SNI: "wikileaks.org" ← 평문! ISP가 볼 수 있음
해결 (Encrypted Client Hello, ECH):
TLS 1.3에서는 SNI도 암호화하여 전송합니다 (2024년 표준화).
ClientHello:
SNI: [암호화됨] ← ISP는 도메인을 알 수 없음
④ 성능 개선 요약
| 항목 | TLS 1.2 | TLS 1.3 | 개선율 |
| 핸드셰이크 RTT | 2 | 1 | 50% 단축 |
| 재방문 시 | 1-RTT | 0-RTT | 100% 단축 |
| 암호 협상 | 복잡 (37개 옵션) | 간소 (5개 옵션) | 관리 용이 |
HTTPS 적용: 개발자 실전 가이드
방법 1: Let's Encrypt (무료, 권장)
Let's Encrypt는 무료 CA로, 자동 갱신을 지원합니다.
Certbot 사용 (Ubuntu/Nginx)
# 1. Certbot 설치
sudo apt update
sudo apt install certbot python3-certbot-nginx
# 2. 인증서 발급 및 Nginx 자동 설정
sudo certbot --nginx -d example.com -d www.example.com
# 결과:
# - /etc/letsencrypt/live/example.com/fullchain.pem (인증서)
# - /etc/letsencrypt/live/example.com/privkey.pem (개인 키)
# - Nginx 설정 자동 수정
# 3. 자동 갱신 설정 (90일마다)
sudo certbot renew --dry-run
Nginx 설정 확인:
server {
listen 443 ssl http2;
server_name example.com;
# Let's Encrypt 인증서
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 보안 강화 설정
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://localhost:3000;
}
}
# HTTP → HTTPS 리다이렉트
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
방법 2: Cloudflare (무료 + CDN)
Cloudflare는 무료 SSL/TLS + CDN + DDoS 방어를 제공합니다.
설정 단계
- Cloudflare 가입 및 도메인 추가
- DNS 네임서버를 Cloudflare로 변경
- SSL/TLS 설정 → Full (strict) 선택
- 완료! Cloudflare가 자동으로 인증서 발급 및 관리
장점:
- 인증서 관리 불필요
- 전 세계 CDN 엣지에서 TLS 종료
- 무료 DDoS 방어
- 자동 HTTP/2, HTTP/3 지원
방법 3: Node.js에서 직접 구현
// server.js
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
// SSL 인증서 로드
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem')
};
app.get('/', (req, res) => {
res.send('Hello HTTPS!');
});
// HTTPS 서버 시작
https.createServer(options, app).listen(443, () => {
console.log('HTTPS server running on port 443');
});
// HTTP → HTTPS 리다이렉트
const http = require('http');
http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
res.end();
}).listen(80);
TLS/SSL 보안 체크리스트
서버 설정
- TLS 1.2 이상만 허용 (TLS 1.0, 1.1 비활성화)
- TLS 1.3 활성화 (가능한 경우)
- 강력한 암호 스위트만 사용 (ECDHE, AES-GCM 등)
- 약한 암호 비활성화 (RC4, DES, MD5 등)
- HSTS 헤더 설정 (브라우저가 항상 HTTPS 사용)
- 인증서 자동 갱신 설정 (Let's Encrypt 90일)
인증서 관리
- 유효 기간 확인 (만료 전 갱신)
- 와일드카드 vs 멀티 도메인 선택
- 중간 인증서 체인 포함 (fullchain.pem)
- 개인 키 보안 (파일 권한 600, 암호화 저장)
모니터링
- SSL Labs 테스트 (https://www.ssllabs.com/ssltest/)
- 인증서 만료 알림 (Cloudflare, Let's Encrypt)
- TLS 버전 사용 현황 분석
실전 테스트: SSL Labs
웹사이트의 TLS 설정을 A+ 등급으로 만들기:
# 1. SSL Labs에서 테스트
https://www.ssllabs.com/ssltest/analyze.html?d=example.com
# 결과 예시:
# - Protocol Support: TLS 1.3 ✓
# - Cipher Strength: 256-bit ✓
# - Forward Secrecy: Yes ✓
# - HSTS: Enabled ✓
# Overall Rating: A+
A+ 등급 달성 조건:
- TLS 1.2, 1.3만 지원
- 강력한 암호 스위트
- HSTS 헤더
- 유효한 인증서 체인
마지막으로
TLS/SSL은 현대 인터넷의 근간입니다. 1990년대 넷스케이프의 SSL부터 2018년 TLS 1.3까지, 30년 가까운 발전을 거쳐 지금의 안전한 인터넷이 만들어졌습니다.
브라우저 주소창의 작은 자물쇠는 단순한 아이콘이 아닙니다. 그 뒤에는:
- 비대칭 암호화로 안전하게 세션 키를 교환하고
- 대칭 암호화로 빠르게 데이터를 전송하며
- 디지털 인증서로 상대방의 신원을 확인하는
복잡하고 정교한 프로토콜이 작동하고 있습니다.
개발자가 할 일은 명확합니다:
① Let's Encrypt로 무료 HTTPS 적용
② TLS 1.2 이상만 허용
③ TLS 1.3 활성화 (권장)
④ HSTS 헤더 설정
⑤ 정기적인 보안 테스트
HTTPS는 더 이상 선택이 아닙니다. Google은 HTTP 사이트에 "안전하지 않음" 경고를 표시하고, 검색 순위에서 불이익을 줍니다. 법적 규제(GDPR, PCI DSS 등)도 암호화를 요구합니다.
지금 당장 Let's Encrypt를 설치하고, 30분 만에 HTTPS를 적용하세요:
sudo certbot --nginx -d yourdomain.com
그 작은 자물쇠가 사용자의 신뢰를, 그리고 당신의 서비스를 지킵니다.
댓글