HTTPS(HTTP Secure)는 HTTP 통신을 암호화하여 안전하게 만드는 프로토콜이다. HTTP는 평문으로 전송되어 중간에서 가로채면 내용을 모두 볼 수 있지만, HTTPS는 암호화하여 제3자가 읽을 수 없게 한다.
현대 웹에서 HTTPS는 선택이 아닌 필수다. 구글은 HTTPS를 검색 순위 요소로 사용하고, 브라우저는 HTTP 사이트에 “안전하지 않음” 경고를 표시한다. 사용자 데이터를 다루는 모든 서비스는 HTTPS를 반드시 사용해야 한다.
HTTP의 보안 문제
HTTP는 세 가지 주요 보안 위협에 노출되어 있다. HTTP 통신은 평문으로 전송되어 누구나 읽을 수 있다. 공격자가 네트워크 패킷을 캡처하면 비밀번호, 신용카드 번호, 개인정보가 그대로 노출된다.
중간자가 메시지를 수정할 수 있다. 악성 스크립트를 삽입하거나, 송금 금액을 바꾸거나, 링크를 변경할 수 있다.
가짜 서버가 정상 서버인 척할 수 있으며 사용자는 진짜 사이트에 접속했다고 믿지만, 실제로는 공격자의 서버에 연결된다.
HTTPS의 동작 원리
HTTPS는 HTTP에 TLS(Transport Layer Security) 계층을 추가한다. HTTP는 애플리케이션에서 TCP로 직접 전달되지만, HTTPS는 애플리케이션에서 TLS를 거쳐 TCP로 전달된다. TLS가 데이터를 암호화하고, 무결성을 검증하며, 서버를 인증한다.
암호화의 기초
암호화는 평문을 암호문으로 변환하는 과정이다. 키를 통해서만 복호화할 수 있으며, 키 없이는 암호문을 평문으로 되돌릴 수 없다.
-
대칭키 암호화: 암호화와 복호화에 같은 키를 사용한다. 장점은 빠르고 효율적이며, AES 암호화는 대용량 데이터를 빠르게 처리가 가능하지만 키 안전하지 않은 채널로 키를 보내면 공격자가 가로챌 수 있다.
-
공개키 암호화: 두 개의 키를 사용하는 방식으로 공개키는 누구에게나 공개하고, 개인키는 본인만 보관한다. 공개키로 암호화하면 개인키로만 복호화할 수 있다. 서버가 공개키를 배포하고, 클라이언트가 공개키로 메시지를 암호화하여 전송하면, 개인키를 가진 서버만 복호화할 수 있다. 공개키는 누구에게나 공개해도 안전하지만 비교적 느리다. 대칭키보다 100-1000배 느려 대용량 데이터에 부적합하다.
-
하이브리드 방식: HTTPS는 대칭키와 공개키 두 방식을 결합한다. 공개키로 대칭키를 안전하게 교환한 후, 대칭키로 실제 데이터를 암호화한다. 공개키의 안전성과 대칭키의 속도를 모두 얻는다.
TLS 핸드셰이크
HTTPS 연결 시작 시 TLS 핸드셰이크가 발생한다. 과정은 다음과 같다:
- ClientHello: 클라이언트가 서버에 TLS 버전과 지원하는 암호화 알고리즘을 알린다.
- ServerHello: 서버가 TLS 버전과 암호화 알고리즘을 선택하고 인증서를 전송한다.
- 인증서 검증: 클라이언트가 인증서가 진짜인지, CA가 서명했는지, 만료되지 않았는지 확인한다.
- 키 교환: 공개키로 대칭키 생성을 위한 비밀 값을 교환한다.
- 완료: 이후 모든 통신은 대칭키로 암호화된다.
TLS 1.3은 1-RTT 핸드셰이크로 빠르게 연결된다. 이전 버전은 2-RTT가 필요했다.
키 교환의 상세 과정
TLS 핸드셰이크에서 가장 중요한 부분은 키 교환이다. Diffie-Hellman 키 교환을 사용하여 안전하게 대칭키를 생성한다.
Diffie-Hellman 키 교환은 두 당사자가 공개 채널을 통해 비밀 키를 공유할 수 있게 한다:
- 클라이언트와 서버가 공개 파라미터(g, p)를 합의한다
- 클라이언트는 비밀 값 a를 생성하고, g^a mod p를 서버에 전송한다
- 서버는 비밀 값 b를 생성하고, g^b mod p를 클라이언트에 전송한다
- 클라이언트는 (g^b)^a mod p를 계산하고, 서버는 (g^a)^b mod p를 계산한다
- 두 값이 같아지며, 이것이 공유 비밀 키가 된다
중요한 점은 중간자가 g^a와 g^b를 볼 수 있어도, a와 b를 알 수 없다는 것이다. 이산 로그 문제의 어려움 때문이다.
**ECDHE (Elliptic Curve Diffie-Hellman Ephemeral)**는 타원곡선을 사용한 Diffie-Hellman의 변형이다. 더 작은 키 크기로 동일한 보안 수준을 제공하며, 일시적(ephemeral) 키를 사용하여 Perfect Forward Secrecy를 제공한다.
Perfect Forward Secrecy (PFS)
Perfect Forward Secrecy는 장기 키가 유출되어도 과거 통신을 복호화할 수 없게 보장한다. ECDHE나 DHE를 사용하면 세션마다 새로운 키를 생성하므로, 서버의 개인키가 유출되어도 과거 세션의 키를 알 수 없다.
PFS가 없는 경우(예: RSA 키 교환), 서버의 개인키가 유출되면 공격자가 저장된 모든 통신을 복호화할 수 있다. PFS를 사용하면 각 세션의 키가 독립적이므로, 한 세션의 키가 유출되어도 다른 세션에는 영향을 주지 않는다.
TLS 버전별 차이점
TLS 1.2는 여전히 널리 사용되지만, 일부 취약점이 있다. TLS 1.3은 다음과 같은 개선사항을 제공한다:
- 1-RTT 핸드셰이크: 이전 버전의 2-RTT에서 1-RTT로 단축
- 0-RTT 재개: 이전에 연결했던 서버와 재연결 시 핸드셰이크 없이 즉시 데이터 전송 가능
- 강제 PFS: 모든 키 교환이 ephemeral이어야 하므로 PFS가 필수
- 불안전한 암호화 스위트 제거: RC4, MD5, SHA-1 등 취약한 알고리즘 제거
- 암호화 스위트 협상 단순화: 서버가 선택하는 방식에서 클라이언트가 제안하는 방식으로 변경
0-RTT의 주의사항
TLS 1.3의 **0-RTT (Zero Round-Trip Time)**는 재연결 시 핸드셰이크 없이 즉시 데이터를 전송할 수 있게 한다. 하지만 **재전송 공격 (Replay Attack)**에 취약하다.
0-RTT로 전송된 데이터는 이전 세션의 키로 암호화되어 있다. 공격자가 이전에 전송된 0-RTT 데이터를 가로채서 나중에 재전송하면, 서버는 이를 유효한 요청으로 처리할 수 있다.
예를 들어, 0-RTT로 “송금 100만원” 요청을 보냈다면, 공격자가 이 요청을 여러 번 재전송하여 여러 번 송금이 실행될 수 있다.
방어 방법:
- idempotent 요청만 0-RTT 사용: 같은 요청을 여러 번 해도 결과가 같은 요청만 0-RTT로 전송한다
- 서버 측 재전송 방지: 서버가 0-RTT 요청의 고유 식별자를 추적하여 중복 요청을 거부한다
- 0-RTT 비활성화: 재전송 공격이 위험한 경우 0-RTT를 비활성화한다
인증서와 신뢰 체인
인증서는 공개키가 진짜로 그 서버의 것임을 증명한다. 인증서에는 도메인, 공개키, 발급자, 유효기간, 디지털 서명이 포함된다.
CA (Certificate Authority)
CA는 인증서를 발급하는 신뢰할 수 있는 기관이다. Let’s Encrypt, DigiCert, Comodo 등이 있다. 발급 과정은 다음과 같다:
- 서버 운영자가 CSR(Certificate Signing Request)을 CA에 제출
- CA가 도메인 소유권을 검증
- CA가 개인키로 인증서에 서명하여 발급
브라우저는 신뢰할 수 있는 CA 목록을 내장하고 있다. CA의 공개키로 서명을 검증한다.
신뢰 체인
인증서는 계층 구조를 가진다. 루트 CA(브라우저에 내장)가 중간 CA에 서명하고, 중간 CA가 서버 인증서에 서명한다. 브라우저는 서버 인증서부터 시작하여 루트 CA까지 체인을 따라가며 검증한다.
인증서 검증 과정
브라우저는 인증서를 받으면 다음을 검증한다:
-
서명 검증: 인증서의 디지털 서명이 CA의 공개키로 검증되는지 확인한다. 서명이 유효하지 않으면 인증서가 변조되었거나 가짜다.
-
체인 검증: 서버 인증서부터 루트 CA까지의 전체 체인을 검증한다. 중간 CA 인증서가 없으면 검증에 실패할 수 있다.
-
유효기간 확인: 인증서가 만료되지 않았는지, 아직 유효하지 않은 인증서는 아닌지 확인한다.
-
도메인 확인: 인증서의 도메인이 실제 접속한 도메인과 일치하는지 확인한다. 와일드카드 인증서는
*.example.com처럼 서브도메인에만 적용된다. -
인증서 취소 상태 확인: 인증서가 취소되었는지 확인한다. OCSP나 CRL을 통해 확인한다.
OCSP와 CRL
인증서가 발급된 후 취소될 수 있다. 예를 들어 개인키가 유출되거나, 도메인 소유권이 변경되면 인증서를 취소해야 한다.
**CRL (Certificate Revocation List)**은 취소된 인증서 목록이다. CA가 주기적으로 업데이트하며, 클라이언트가 다운로드하여 확인한다. 문제는 CRL이 커지면 다운로드 시간이 오래 걸리고, 업데이트 주기 동안 취소된 인증서를 사용할 수 있다는 점이다.
**OCSP (Online Certificate Status Protocol)**는 실시간으로 인증서 상태를 확인한다. 클라이언트가 OCSP 서버에 인증서 상태를 질의하면 즉시 응답을 받는다. 하지만 OCSP 서버에 대한 요청이 추가로 발생하고, OCSP 서버가 다운되면 인증서 검증이 실패할 수 있다.
OCSP Stapling은 서버가 OCSP 응답을 미리 받아서 TLS 핸드셰이크 시 함께 전송한다. 클라이언트는 OCSP 서버에 직접 요청하지 않고 서버로부터 받은 응답을 검증한다. 이렇게 하면 클라이언트의 추가 요청이 없고, OCSP 서버 부하도 줄인다.
인증서 고정 (Certificate Pinning)
인증서 고정은 애플리케이션이 특정 CA나 인증서만 신뢰하도록 하는 기법이다. 모바일 앱에서 특히 유용하다.
예를 들어, 앱이 특정 CA의 인증서만 신뢰하도록 설정하면, 중간자 공격자가 다른 CA의 인증서를 사용해도 연결이 거부된다. 하지만 인증서가 만료되거나 CA가 변경되면 앱 업데이트가 필요하다는 단점이 있다.
**HPKP (HTTP Public Key Pinning)**는 웹에서 사용했지만, 잘못 설정하면 사이트가 완전히 차단될 수 있어 2018년에 폐기되었다. 대신 Expect-CT 헤더나 Certificate Transparency를 사용한다.
암호화 알고리즘
HTTPS는 여러 암호화 알고리즘을 사용한다.
대칭키 알고리즘
**AES (Advanced Encryption Standard)**는 가장 널리 사용되는 대칭키 알고리즘이다. AES-128은 128비트 키를, AES-256은 256비트 키를 사용한다. AES는 블록 암호로, 128비트 블록 단위로 암호화한다.
AES는 여러 모드를 지원한다:
- CBC (Cipher Block Chaining): 이전 블록의 암호문을 다음 블록의 XOR 입력으로 사용한다. 초기화 벡터(IV)가 필요하며, 각 블록이 이전 블록에 의존한다.
- GCM (Galois/Counter Mode): 카운터 모드와 인증을 결합한다. 암호화와 동시에 무결성을 검증하므로 별도의 MAC이 필요 없다. TLS 1.2 이상에서 권장된다.
ChaCha20은 모바일에서 효율적인 스트림 암호다. AES 하드웨어 가속이 없는 환경에서 빠르며, 특히 ARM 프로세서에서 성능이 좋다. ChaCha20-Poly1305는 ChaCha20과 Poly1305 MAC을 결합한 AEAD(Authenticated Encryption with Associated Data) 방식이다.
공개키 알고리즘
RSA는 전통적인 공개키 알고리즘으로, 키 크기가 커야 안전하다. 1024비트 RSA는 더 이상 안전하지 않으며, 최소 2048비트를 사용해야 한다. 3072비트나 4096비트를 사용하면 더 안전하지만, 키 생성과 연산이 느려진다.
RSA의 안전성은 큰 수의 인수분해 어려움에 기반한다. 두 개의 큰 소수를 곱한 수는 쉽게 계산할 수 있지만, 그 결과를 다시 소인수분해하는 것은 매우 어렵다.
**ECDSA (Elliptic Curve Digital Signature Algorithm)**는 타원곡선 암호를 사용한다. 작은 키로도 강력한 보안을 제공한다. 256비트 ECDSA가 3072비트 RSA와 동등한 보안 수준을 제공한다. 키 크기가 작아서 전송과 저장이 효율적이며, 연산도 빠르다.
타원곡선 암호는 타원곡선 위의 점들 간의 연산을 사용한다. 이산 로그 문제의 변형인 타원곡선 이산 로그 문제를 기반으로 하며, 같은 보안 수준에서 RSA보다 훨씬 작은 키를 사용할 수 있다.
해시 알고리즘과 MAC
SHA-256은 데이터의 지문을 생성한다. 같은 입력은 항상 같은 출력을 내지만, 출력에서 입력을 역추적할 수 없다. 메시지 무결성 검증에 사용된다.
해시 함수는 다음 성질을 만족해야 한다:
- 일방향성: 해시값에서 원본을 역산할 수 없다
- 충돌 저항성: 같은 해시값을 가진 두 개의 다른 입력을 찾기 어렵다
- 눈사태 효과: 입력의 작은 변화가 해시값을 완전히 바꾼다
**HMAC (Hash-based Message Authentication Code)**는 해시 함수와 비밀 키를 결합하여 메시지 인증 코드를 생성한다. 메시지가 변조되지 않았고, 특정 키를 가진 사람이 생성했음을 보장한다.
Poly1305는 ChaCha20과 함께 사용되는 MAC 알고리즘이다. 매우 빠르고 효율적이며, 하드웨어 가속이 없어도 좋은 성능을 제공한다.
HTTPS의 성능
HTTPS는 HTTP보다 느릴 수 있지만, 최적화하면 영향을 최소화할 수 있다.
초기 연결 시 암호화 협상으로 인해 1-2 RTT가 추가된다. HTTP는 TCP 핸드셰이크 후 바로 요청하지만, HTTPS는 TCP 핸드셰이크 후 TLS 핸드셰이크를 거쳐야 한다. TLS 1.3은 1-RTT로 줄였고, 0-RTT 재개로 재연결 시 핸드셰이크를 생략한다.
대칭키 암호화는 매우 빠르다. 현대 CPU는 AES 하드웨어 가속을 지원하여 오버헤드가 1-2%에 불과하다.
최적화 방법
- HTTP/2 사용: 연결을 재사용하여 핸드셰이크 비용을 분산한다.
- 세션 재개: TLS 세션을 캐시하여 재연결 시 빠르게 연결한다.
- OCSP Stapling: 인증서 상태 확인을 서버가 대신하여 클라이언트 요청을 줄인다.
- CDN 사용: 엣지 서버에서 TLS를 종료하여 사용자와 가까운 곳에서 핸드셰이크한다.
HTTPS 적용 방법
인증서 발급
Let’s Encrypt는 무료 인증서를 제공한다. 90일마다 자동 갱신되며, Certbot으로 발급할 수 있다. 상용 CA는 유료지만 확장 검증(EV), 와일드카드, 보증 등을 제공한다.
인증서 종류
검증 수준별:
- DV (Domain Validation): 도메인 소유권만 확인. 가장 빠르고 저렴하다 (Let’s Encrypt)
- OV (Organization Validation): 회사 정보 확인. 신뢰도가 높다
- EV (Extended Validation): 엄격한 법적 검증. 주소창에 회사명 표시
도메인별:
- 단일 도메인: example.com 하나만
- 와일드카드: *.example.com (모든 서브도메인)
- 멀티 도메인 (SAN): 여러 도메인 (example.com, example.org, example.net)
인증서 갱신
Let’s Encrypt는 90일마다 갱신된다. Certbot이 자동 갱신을 처리한다. 만료 30일, 7일 전에 알림을 설정하여 갱신을 놓치지 않도록 한다.
HTTPS 강제
HTTP로 접속하면 HTTPS로 리다이렉트한다. HSTS (HTTP Strict Transport Security) 헤더로 브라우저가 항상 HTTPS를 사용하게 한다. Strict-Transport-Security: max-age=31536000; includeSubDomains로 설정하면 1년 동안 이 도메인은 HTTPS만 사용한다. 브라우저가 자동으로 HTTP를 HTTPS로 변환한다.
Mixed Content 방지
HTTPS 페이지에서 HTTP 리소스를 로드하면 경고가 발생한다. 모든 리소스를 HTTPS로 변경하거나, 프로토콜을 생략한다(예: //cdn.example.com/script.js).
TLS 공격과 방어
TLS도 여러 공격에 노출될 수 있다. 주요 공격 기법과 방어 방법을 이해해야 한다.
BEAST (Browser Exploit Against SSL/TLS)
BEAST는 TLS 1.0과 SSL 3.0의 CBC 모드 취약점을 이용한 공격이다. 초기화 벡터(IV)를 예측 가능하게 만들어 암호문을 복호화한다. 방어 방법은 TLS 1.1 이상을 사용하거나, RC4를 사용하는 것이다. 하지만 RC4 자체도 취약하므로 TLS 1.2 이상을 사용하는 것이 좋다.
POODLE (Padding Oracle On Downgraded Legacy Encryption)
POODLE은 SSL 3.0의 패딩 검증 취약점을 이용한 공격이다. 공격자가 클라이언트를 SSL 3.0으로 다운그레이드시킨 후 패딩 오라클 공격을 수행한다. 방어 방법은 SSL 3.0을 완전히 비활성화하는 것이다.
Heartbleed
Heartbleed는 OpenSSL의 Heartbeat 확장 구현 버그다. 메모리에서 최대 64KB의 데이터를 읽을 수 있어, 서버의 개인키나 세션 쿠키가 유출될 수 있다. 방어 방법은 OpenSSL을 최신 버전으로 업데이트하는 것이다.
FREAK (Factoring Attack on RSA-EXPORT Keys)
FREAK는 수출용 약한 RSA 키를 강제로 사용하게 만드는 공격이다. 512비트 RSA 키는 쉽게 인수분해할 수 있다. 방어 방법은 약한 키 교환 알고리즘을 비활성화하는 것이다.
로밍 공격 (Downgrade Attack)
공격자가 클라이언트와 서버 사이에서 통신을 가로채고, 양쪽에 서로 다른 TLS 버전을 사용하게 만든다. 예를 들어 클라이언트에는 TLS 1.2를, 서버에는 TLS 1.0을 사용하게 하여 취약한 버전으로 통신하게 만든다.
방어 방법은 TLS_FALLBACK_SCSV를 사용하는 것이다. 클라이언트가 이 신호를 보내면 서버는 더 낮은 버전으로 다운그레이드하지 않는다.
암호화 스위트 선택
서버는 클라이언트가 제안한 암호화 스위트 중에서 선택한다. 약한 암호화 스위트를 선택하면 보안이 약해진다. 다음을 고려해야 한다:
- 강력한 키 교환: ECDHE나 DHE를 사용하여 PFS를 제공한다
- 강력한 대칭키 암호화: AES-128 이상을 사용한다
- 강력한 해시 알고리즘: SHA-256 이상을 사용한다
- 약한 알고리즘 제거: RC4, MD5, SHA-1, 3DES 등을 제거한다
HTTPS의 한계
-
SNI (Server Name Indication): TLS 핸드셰이크 시 도메인 이름이 평문으로 전송된다. ISP가 어떤 사이트에 접속하는지 알 수 있다. **ESNI (Encrypted SNI)**가 이를 해결하지만 아직 표준화 중이다.
-
인증서 관리: 인증서 만료를 관리해야 한다. 만료되면 사이트가 접속 불가능해진다. 자동 갱신을 설정해야 한다.
-
비용: 무료 인증서가 있지만, EV 인증서나 멀티 도메인 인증서는 비용이 발생한다.
-
메타데이터 노출: 암호화되어도 패킷 크기, 타이밍 정보 등은 노출될 수 있다. 이를 통해 어떤 사이트에 접속하는지 추론할 수 있다.