HTTPS: TLS로 HTTP의 기밀성·무결성·서버 인증을 제공하는 통신 방식
HTTPS는 HTTP 메시지를 전송 계층 보안(TLS)으로 보호하는 통신 방식이다. 인증서와 공개 키 암호를 사용해 서버를 인증하고 세션 키를 합의한 뒤, 대칭 키 암호로 요청과 응답을 보호한다. HTTPS를 사용해도 애플리케이션의 인증·인가와 입력 검증까지 자동으로 해결되지는 않는다.
By 송민성
1. 개념
HTTPS(Hypertext Transfer Protocol Secure)는 HTTP를 전송 계층 보안(TLS, Transport Layer Security) 연결 위에서 주고받는 방식이다.
HTTP = HTTP 메시지 + 평문 전송
HTTPS = HTTP 메시지 + TLS 보호HTTPS는 다음 보안 속성을 제공한다.
- 기밀성(Confidentiality): 통신 내용을 암호화해 제3자가 읽기 어렵게 한다.
- 무결성(Integrity): 전송 중 데이터가 변경됐는지 검출한다.
- 인증(Authentication): 클라이언트가 접속한 서버의 도메인과 인증서가 일치하는지 확인한다.
클라이언트
│
│ TLS로 암호화된 HTTP 요청
│──────────────────────────> 서버
│
│ TLS로 암호화된 HTTP 응답
│<──────────────────────────HTTPS는 별도의 애플리케이션 프로토콜이 아니다. HTTP의 메서드, 헤더, 상태 코드, 본문 의미는 그대로 유지된다.
GET /users/42 HTTP/1.1
Host: example.com
Authorization: Bearer token네트워크에서는 이 메시지가 그대로 노출되지 않고 TLS 레코드(TLS Record)에 담겨 암호화된다.
과거에는 보안 프로토콜을 보안 소켓 계층(SSL, Secure Sockets Layer)이라고 불렀다. SSL 2.0과 SSL 3.0은 안전하지 않으며 현재 HTTPS는 TLS를 사용한다. 실무에서 말하는 SSL 인증서도 정확히는 TLS 인증서다.
2. 왜 사용하는가
HTTP를 평문으로 전송하면 같은 네트워크를 관찰할 수 있는 공격자가 요청과 응답을 읽거나 변경할 수 있다.
POST /login
username=kim&password=secretHTTPS는 다음 공격 위험을 줄인다.
도청 방지
로그인 정보, 쿠키, 개인 정보, API 응답을 암호화한다. 공격자가 패킷을 수집해도 세션 키가 없으면 원문을 해독하기 어렵다.
변조 방지
메시지 인증 코드와 인증 암호 방식(AEAD, Authenticated Encryption with Associated Data)을 사용해 데이터 변경을 검출한다. 공격자가 응답의 자바스크립트나 다운로드 파일을 바꾸면 TLS 검증이 실패한다.
서버 사칭 방지
클라이언트는 서버 인증서의 도메인, 유효 기간, 인증서 체인(Certificate Chain)을 검증한다. 이를 통해 example.com을 사칭하는 서버와 연결될 가능성을 낮춘다.
웹 플랫폼 기능 사용
브라우저는 다음 기능을 보안 컨텍스트(Secure Context)에서만 제공하거나 강하게 제한한다.
- 서비스 워커(Service Worker)
- 위치 정보(Geolocation)
- 클립보드(Clipboard)의 일부 기능
- HTTP/2와 HTTP/3의 일반적인 브라우저 사용
Secure속성이 있는 쿠키
HTTPS는 전송 구간을 보호하지만 다음 문제는 해결하지 않는다.
- SQL 삽입(SQL Injection)
- 크로스 사이트 스크립팅(XSS)
- 취약한 비밀번호
- 잘못된 접근 권한
- 감염된 클라이언트나 서버
- 서버 로그에 기록된 민감 정보
3. 동작 원리
HTTPS 연결은 크게 TCP 또는 QUIC 연결, TLS 핸드셰이크(TLS Handshake), HTTP 통신 순서로 진행된다.
3.1 주소 확인과 연결
클라이언트는 도메인을 도메인 이름 시스템(DNS, Domain Name System)으로 조회한 뒤 서버에 연결한다.
https://example.com
│
├─ DNS 조회
├─ 서버 연결
├─ TLS 핸드셰이크
└─ HTTP 요청과 응답HTTP/1.1과 HTTP/2는 일반적으로 TCP 위에서 TLS를 사용한다. HTTP/3는 UDP 기반 QUIC에 TLS 1.3의 보안 절차를 통합한다.
HTTPS의 기본 포트는 443이다.
3.2 클라이언트 헬로
클라이언트는 클라이언트 헬로(ClientHello)를 보낸다. 주요 정보는 다음과 같다.
- 지원하는 TLS 버전
- 지원하는 암호 스위트(Cipher Suite)
- 키 합의용 공개 값
- 클라이언트 난수
- 접속하려는 서버 이름을 나타내는 SNI(Server Name Indication)
- HTTP 프로토콜을 협상하는 ALPN(Application-Layer Protocol Negotiation)
ALPN을 통해 h2가 선택되면 HTTP/2를, http/1.1이 선택되면 HTTP/1.1을 사용한다.
3.3 서버 헬로와 인증서 전달
서버는 서버 헬로(ServerHello)에서 사용할 TLS 버전과 암호 스위트를 선택한다. 이어서 서버 인증서와 키 합의 정보를 전달한다.
인증서는 특정 도메인과 공개 키의 관계를 증명한다.
루트 인증 기관 인증서
└─ 중간 인증 기관 인증서
└─ example.com 서버 인증서브라우저는 다음 항목을 확인한다.
- 인증서의 주체 대체 이름(SAN, Subject Alternative Name)에 접속 도메인이 포함됐는가
- 인증서가 유효 기간 안에 있는가
- 인증서 체인이 신뢰하는 루트 인증 기관(CA, Certificate Authority)으로 이어지는가
- 체인의 전자 서명(Digital Signature)이 유효한가
- 인증서가 서버 인증 용도로 발급됐는가
인증서는 서버 운영자의 신원이나 서비스의 안전성을 모두 보장하지 않는다. 해당 도메인과 공개 키의 연결을 검증하는 역할이 중심이다.
3.4 세션 키 합의
TLS 1.3은 일반적으로 임시 디피-헬먼 키 교환(Ephemeral Diffie–Hellman Key Exchange)을 사용한다.
클라이언트와 서버는 공개 값을 교환하고, 네트워크에 직접 전송하지 않은 공유 비밀(Shared Secret)을 각각 계산한다. 이 값에서 실제 통신에 사용할 세션 키(Session Key)를 유도한다.
클라이언트 개인 값 + 서버 공개 값
│
└─ 공유 비밀
서버 개인 값 + 클라이언트 공개 값
│
└─ 같은 공유 비밀공개 키 암호는 서버 인증과 키 합의에 사용된다. 이후 HTTP 데이터는 계산 비용이 낮은 대칭 키 암호(Symmetric-key Cryptography)로 보호한다.
임시 키를 사용하는 키 합의는 순방향 비밀성(Forward Secrecy)을 제공한다. 나중에 서버의 장기 개인 키가 유출돼도 과거 통신의 임시 세션 키까지 바로 복원되지는 않는다.
3.5 암호화된 HTTP 통신
핸드셰이크가 끝나면 HTTP 요청과 응답이 TLS 레코드로 전달된다.
TLS 1.3의 대표적인 암호 스위트는 다음과 같다.
TLS_AES_128_GCM_SHA256구성 요소의 역할은 다음과 같다.
- AES-128-GCM: 암호화와 무결성 검증
- SHA-256: 키 유도와 핸드셰이크 검증에 사용하는 해시 함수
TLS는 각 레코드의 인증 태그(Authentication Tag)를 검증한다. 암호문이나 관련 데이터가 변경되면 검증이 실패하고 연결이 종료된다.
3.6 세션 재개
한 번 연결했던 서버에 다시 접속할 때는 세션 재개(Session Resumption)를 사용할 수 있다. TLS 1.3에서는 서버가 발급한 세션 티켓(Session Ticket)을 이용해 전체 핸드셰이크 비용을 줄인다.
조기 데이터(0-RTT Early Data)를 사용하면 핸드셰이크 완료 전에 애플리케이션 데이터를 보낼 수 있다. 그러나 0-RTT 데이터는 재전송 공격(Replay Attack)에 노출될 수 있으므로 결제나 상태 변경 요청에 사용하면 안 된다.
4. 코드 예제
Node.js의 내장 HTTPS 모듈로 로컬 HTTPS 서버를 실행한다.
4.1 개발용 인증서 생성
다음 명령은 localhost와 127.0.0.1을 주체 대체 이름에 포함한 자체 서명 인증서(Self-signed Certificate)를 생성한다.
openssl req \
-x509 \
-newkey rsa:2048 \
-sha256 \
-nodes \
-days 30 \
-keyout localhost-key.pem \
-out localhost-cert.pem \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"자체 서명 인증서는 공인 인증 기관이 서명하지 않았으므로 브라우저와 운영체제가 기본적으로 신뢰하지 않는다. 로컬 개발과 학습 목적으로만 사용한다.
4.2 HTTPS 서버 작성
server.ts 파일을 만든다.
import { createServer } from "node:https";
import { readFileSync } from "node:fs";
const server = createServer(
{
key: readFileSync("localhost-key.pem"),
cert: readFileSync("localhost-cert.pem"),
minVersion: "TLSv1.2",
},
(request, response) => {
response.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
"Strict-Transport-Security": "max-age=31536000",
});
response.end(
JSON.stringify({
method: request.method,
url: request.url,
encrypted: request.socket.encrypted,
}),
);
},
);
server.listen(8443, "127.0.0.1", () => {
console.log("https://localhost:8443");
});실행한다.
npx tsx server.ts다른 터미널에서 생성한 인증서를 신뢰할 인증서로 지정해 요청한다.
curl \
--cacert localhost-cert.pem \
https://localhost:8443/health응답은 다음과 같다.
{"method":"GET","url":"/health","encrypted":true}인증서 검증을 생략하는 curl -k나 Node.js의 rejectUnauthorized: false는 중간자 공격 방어를 제거한다. 운영 코드에서 사용하지 않는다.
4.3 TLS 연결 정보 확인
OpenSSL 클라이언트로 인증서와 협상된 프로토콜을 확인할 수 있다.
openssl s_client \
-connect localhost:8443 \
-servername localhost \
-CAfile localhost-cert.pem \
-alpn http/1.1출력에서 다음 항목을 확인한다.
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Verify return code: 0 (ok)실제 암호 스위트는 운영체제, OpenSSL 버전, Node.js 설정에 따라 달라질 수 있다.
5. 시간 복잡도 또는 성능 특성
HTTPS는 알고리즘의 입력 크기를 분석하는 일반적인 시간 복잡도보다 왕복 시간(RTT, Round-Trip Time), 암호 연산, 연결 재사용 관점에서 평가한다.
연결 지연
HTTP/1.1이나 HTTP/2에서 새 HTTPS 연결을 만들면 TCP 연결과 TLS 핸드셰이크가 필요하다.
TCP 연결: 일반적으로 1 RTT
TLS 1.3 전체 연결: 일반적으로 1 RTT
TLS 1.3 세션 재개: 0-RTT 사용 가능TLS 1.3의 전체 핸드셰이크에서는 클라이언트가 서버 응답을 받은 뒤 암호화된 애플리케이션 데이터를 보낼 수 있으므로 보통 1 RTT가 필요하다. TCP 연결까지 새로 만들어야 한다면 애플리케이션 데이터 전송 전 지연이 추가된다.
HTTP/3의 QUIC은 전송 연결과 TLS 1.3 핸드셰이크를 통합해 별도의 TCP 핸드셰이크를 사용하지 않는다.
암호 연산
핸드셰이크에는 인증서 서명 검증과 키 합의 같은 공개 키 연산이 들어간다. 연결 후 데이터 암호화에는 대칭 키 암호를 사용한다.
데이터 크기를 n바이트라고 하면 암호화와 복호화 작업량은 데이터 양에 비례하므로 O(n)이다. 실제 처리량은 CPU의 AES 명령어 지원, 선택한 암호 스위트, 레코드 크기에 영향을 받는다.
연결 재사용
연결마다 새 핸드셰이크를 수행하면 지연과 CPU 사용량이 증가한다. 다음 기능으로 비용을 줄일 수 있다.
- HTTP 지속 연결(Keep-Alive)
- HTTP/2 다중화(Multiplexing)
- HTTP/3 다중 스트림
- TLS 세션 재개
- 인증서 체인 크기 최적화
HTTP/2는 하나의 TCP 연결에서 여러 요청을 동시에 처리한다. HTTP/3는 스트림별 전송을 제공해 한 스트림의 패킷 손실이 다른 스트림 전체를 막는 TCP 수준의 헤드 오브 라인 블로킹(Head-of-Line Blocking)을 줄인다.
6. 실무 사용 사례
리버스 프록시에서 TLS 종료
운영 환경에서는 Nginx, 로드 밸런서, 콘텐츠 전송 네트워크(CDN)가 TLS 연결을 처리하고 애플리케이션 서버로 요청을 전달하는 경우가 많다. 이를 TLS 종료(TLS Termination)라고 한다.
브라우저
│ HTTPS
▼
로드 밸런서 또는 리버스 프록시
│ HTTP 또는 HTTPS
▼
애플리케이션 서버프록시 뒤에서도 보안이 필요한 네트워크라면 내부 구간에 HTTPS나 상호 TLS(mTLS, Mutual TLS)를 적용한다.
애플리케이션은 X-Forwarded-Proto 같은 헤더로 원래 요청이 HTTPS였는지 판단할 수 있다. 이 헤더는 신뢰하는 프록시가 덮어쓰도록 설정해야 한다. 외부 클라이언트가 보낸 값을 그대로 신뢰하면 보안 판단이 우회될 수 있다.
인증서 자동 발급과 갱신
공인 웹 서비스는 신뢰받는 인증 기관에서 인증서를 발급받는다. ACME(Automatic Certificate Management Environment)를 지원하는 도구를 사용하면 발급과 갱신을 자동화할 수 있다.
운영 점검 항목은 다음과 같다.
- 인증서 만료 전 자동 갱신
- 중간 인증서 체인 제공
- 개인 키 접근 권한 제한
- 갱신 후 서버 재적용
- 만료일과 갱신 실패 모니터링
HTTP를 HTTPS로 리다이렉트
평문 HTTP 요청은 HTTPS 주소로 이동시킨다.
HTTP/1.1 308 Permanent Redirect
Location: https://example.com/account308 Permanent Redirect는 요청 메서드와 본문을 유지한다. 301과 302는 클라이언트 동작에 따라 POST가 GET으로 바뀔 수 있다.
리다이렉트가 적용되기 전 첫 HTTP 요청은 평문이다. HTTP 엄격 전송 보안(HSTS, HTTP Strict Transport Security)을 사용하면 이후 브라우저가 해당 도메인에 HTTPS로만 접속하도록 지시할 수 있다.
Strict-Transport-Security: max-age=31536000; includeSubDomainsincludeSubDomains를 사용하기 전 모든 하위 도메인이 HTTPS를 지원하는지 확인해야 한다.
쿠키 보호
인증 쿠키에는 최소한 다음 속성을 검토한다.
Set-Cookie: session=token; Secure; HttpOnly; SameSite=Lax; Path=/Secure: HTTPS 요청에서만 쿠키 전송HttpOnly: 자바스크립트의 쿠키 접근 제한SameSite: 교차 사이트 요청에서 쿠키 전송 제한
HTTPS만으로 세션 탈취를 완전히 막을 수는 없다. 크로스 사이트 스크립팅, 토큰 노출, 취약한 세션 관리도 별도로 방어해야 한다.
서비스 간 상호 인증
일반적인 HTTPS에서는 클라이언트가 서버 인증서를 확인한다. 상호 TLS에서는 서버도 클라이언트 인증서를 검증한다.
일반 HTTPS:
클라이언트 ── 서버 인증서 검증 ──> 서버
상호 TLS:
클라이언트 <── 양쪽 인증서 검증 ──> 서버상호 TLS는 내부 API, 서비스 메시(Service Mesh), 기업 네트워크처럼 서비스 신원을 강하게 확인해야 하는 환경에서 사용한다.
7. 주의할 점
인증서 오류를 무시하지 않는다
인증서 검증 실패는 만료, 도메인 불일치, 잘못된 인증서 체인, 신뢰할 수 없는 인증 기관을 의미할 수 있다.
hostname mismatch
certificate has expired
unable to verify the first certificate검증을 비활성화하는 대신 인증서와 서버 이름 설정을 수정한다.
혼합 콘텐츠를 제거한다
HTTPS 페이지에서 HTTP 이미지, 스크립트, API를 불러오면 혼합 콘텐츠(Mixed Content)가 발생한다.
<script src="http://example.com/app.js"></script>스크립트와 같은 활성 콘텐츠는 브라우저가 차단할 수 있다. 모든 하위 자원도 HTTPS로 제공해야 한다.
URL과 메타데이터는 완전히 숨겨지지 않는다
HTTPS는 HTTP 본문과 대부분의 헤더를 암호화하지만 통신에 필요한 모든 메타데이터를 숨기지는 않는다.
관찰자는 다음 정보를 알아낼 수 있다.
- 접속한 IP 주소
- 포트
- 패킷 크기와 통신 시점
- 전체 전송량
- 설정에 따라 노출되는 서버 이름 정보
URL의 경로와 쿼리 문자열은 TLS 내부에서 암호화된다. 그러나 URL은 브라우저 방문 기록, 서버 로그, 분석 도구, 리퍼러(Referrer) 등에 남을 수 있다. 비밀번호나 장기 비밀 값을 쿼리 문자열에 넣지 않는다.
개인 키를 보호한다
인증서 자체는 공개 정보지만 개인 키(Private Key)는 비밀 정보다.
인증서: 배포 가능
개인 키: 접근 제한 필요개인 키를 저장소에 커밋하지 않고, 비밀 관리 시스템이나 제한된 파일 권한으로 관리한다. 키가 유출되면 새 키와 인증서를 발급하고 기존 인증서를 폐기한다.
최신 프로토콜을 사용한다
SSL과 오래된 TLS 버전은 사용하지 않는다. 서버와 클라이언트 호환성을 확인한 뒤 TLS 1.2 이상을 허용하고 TLS 1.3을 우선 사용한다.
암호 스위트를 직접 구성할 때는 임의 조합보다 운영체제, 웹 서버, 클라우드 공급자가 제공하는 검증된 최신 기본값을 우선한다.
8. 핵심 정리
- HTTPS는 HTTP를 TLS 연결 위에서 전송하는 방식이다.
- TLS는 기밀성, 무결성, 서버 인증을 제공한다.
- 인증서는 도메인과 공개 키의 관계를 인증서 체인으로 검증한다.
- 공개 키 암호는 인증과 키 합의에, 대칭 키 암호는 실제 HTTP 데이터 보호에 사용한다.
- TLS 1.3 전체 핸드셰이크는 일반적으로 1 RTT가 필요하며 연결 재사용과 세션 재개로 비용을 줄일 수 있다.
- HTTPS는 전송 구간을 보호하지만 애플리케이션 인증·인가, 입력 검증, XSS 방어를 대신하지 않는다.
- 운영 환경에서는 인증서 자동 갱신, HSTS, 안전한 쿠키, 혼합 콘텐츠 제거, 개인 키 보호를 함께 적용한다.