인터넷으로 데이터를 주고받을 때, 마치 엽서를 우편함에 넣어 보내는 것과 같아요. 누구든 중간에 엽서 내용을 볼 수 있죠. 이처럼 중요한 데이터가 안전하게 전달되도록 돕는 것이 바로 보안 헤더와 암호화입니다.
보안 헤더는 웹 브라우저와 서버가 서로 지켜야 할 약속(규칙)을 정하는 역할을 해요. 예를 들어, '이 웹사이트는 무조건 HTTPS로만 접속해야 해!' 같은 규칙을 알려주는 거죠.
암호화는 데이터 내용을 알아볼 수 없도록 꽁꽁 싸매는 거예요. 마치 비밀 편지를 암호로 쓰는 것처럼요. 이렇게 하면 제3자가 중간에 데이터를 가로채도 내용을 읽을 수 없게 됩니다.
웹을 안전하게 지켜주는 암호화 기술에는 SSL(Secure Sockets Layer) 과 TLS(Transport Layer Security) 가 있어요. 쉽게 말해, SSL이 먼저 나왔고, TLS는 SSL의 업그레이드 버전이라고 생각하면 돼요.
우리가 흔히 'SSL 인증서'라고 부르지만, 실제로 웹 통신을 암호화하는 건 이 인증서를 서버에 설치해서 TLS 프로토콜을 사용하게 하는 거예요.
| 프로토콜 이름 | 주요 특징 | 지금 사용해도 될까요? |
|---|---|---|
| SSL 3.0 | 초창기 암호화 기술 | 사용하지 마세요! 보안에 취약해요. |
| TLS 1.0 | SSL 3.0을 개선했지만 여전히 오래된 기술 | 아주 오래된 환경이 아니라면 사용하지 마세요. |
| TLS 1.2 | 강력한 암호화, 빠르고 안전함 | 지금 대부분의 웹사이트에서 쓰고 있어요! |
| TLS 1.3 | 더 빠르고, 더 안전한 최신 기술 | 가장 추천하는 버전이에요! |
💡 웹사이트 서버에서는 최소한 TLS 1.2 이상을 사용해야 하고, 가능하면 TLS 1.3만 허용해서 최고 수준의 보안과 속도를 확보하는 것이 좋아요.
HTTPS는 웹사이트 주소 앞에 🔓'자물쇠' 모양이 생기고 'https://'로 시작하게 하는 거예요. 이걸 적용하려면 다음 과정을 거쳐야 해요.
인증서 발급받기:
sudo certbot certonly --standalone -d example.com -d www.example.com이 명령어를 입력하면 example.com과 www.example.com 도메인에 대한 인증서를 자동으로 발급받을 수 있어요. 발급된 인증서는 fullchain.pem(인증서 파일)과 privkey.pem(개인키 파일)이라는 이름으로 저장됩니다.서버에 인증서 설치하기:
발급받은 인증서 파일을 웹사이트가 돌아가는 서버에 설정해 줘야 해요. 서버 종류마다 설정 방법이 조금씩 달라요.
NGINX (엔진엑스) 서버 예시:
server {
listen 443 ssl; # 443 포트로 HTTPS 접속을 받아요
server_name example.com; # 이 도메인에 적용해요
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; # TLS 1.2와 1.3만 허용해요
ssl_ciphers HIGH:!aNULL:!MD5; # 안전한 암호화 방식만 사용해요
# 기타 필요한 설정들...
}
Apache (아파치) 서버 예시:
<VirtualHost *:443> # 443 포트로 HTTPS 접속을 받아요
ServerName example.com # 이 도메인에 적용해요
SSLEngine on # SSL/TLS 기능을 켜요
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem # 인증서 파일 위치
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem # 개인키 파일 위치
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 # SSLv3, TLSv1, TLSv1.1은 사용하지 않아요
SSLCipherSuite HIGH:!aNULL:!MD5 # 안전한 암호화 방식만 사용해요
# 기타 필요한 설정들...
</VirtualHost>
Express.js (Node.js) 웹 애플리케이션 예시:
const fs = require('fs');
const https = require('https'); // HTTPS 모듈을 불러와요
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'), // 개인키 파일 읽기
cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem'), // 인증서 파일 읽기
};
https.createServer(options, app).listen(443, () => { // HTTPS 서버를 443 포트로 시작
console.log('HTTPS server running on port 443');
});
인증서 자동 갱신 설정하기:
certbot renew 명령을 컴퓨터가 알아서 실행하도록 설정(크론탭 등록)해두면 편리해요.0 0 * * * certbot renew --quiet이 명령은 매일 자정(새벽 0시)에 certbot renew를 조용히 실행하라는 뜻이에요.제대로 적용되었는지 확인하기:
openssl이라는 명령어를 사용해서 여러분의 웹사이트가 어떤 TLS 버전을 쓰고 있는지, 암호화 강도는 어떤지 확인할 수 있어요.openssl s_client -connect example.com:443 -tls1_2Tip: 사람들이
http://example.com으로 접속해도 자동으로https://example.com으로 연결되도록 서버 설정을 해주는 걸 잊지 마세요! (NGINX에서는return 301 https://$host$request_uri;같은 설정을 사용해요.)
HTTP는 웹에서 데이터를 주고받는 규칙인데, 버전이 계속 업그레이드되고 있어요. 최신 버전들은 보안과 속도를 모두 잡았답니다.
HTTP/2는 기존 HTTP/1.1보다 웹페이지를 훨씬 빠르게 로딩할 수 있게 해줘요.
HTTP/3는 아직 초기 단계이지만, 앞으로 웹의 표준이 될 강력한 프로토콜이에요.
여러분의 웹사이트에 HTTP/2와 HTTP/3를 적용하려면, 사용 중인 서버가 이 기술들을 지원해야 해요.
1) NGINX (엔진엑스)에서 HTTP/2 활성화하기
server {
listen 443 ssl http2; # 443 포트로 HTTPS와 HTTP/2를 함께 사용해요
server_name example.com;
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;
add_header Alt-Svc 'h2=":443"'; # 클라이언트에게 HTTP/2를 지원한다고 알려줘요
}
listen ... http2; 부분이 HTTP/2를 켜는 설정이에요.
2) NGINX (엔진엑스)에서 HTTP/3 (QUIC) 활성화하기
server {
listen 443 ssl http2;
listen 443 quic reuseport; # QUIC 프로토콜을 사용하고, 443 포트를 재사용해요
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.3; # QUIC는 TLS 1.3만 사용해요
ssl_prefer_server_ciphers off;
ssl_ciphers TLS_AES_128_GCM_SHA256; # 사용할 암호화 방식
add_header Alt-Svc 'h3-23=":443"'; # 클라이언트에게 HTTP/3를 지원한다고 알려줘요
add_header QUIC-Status $quic;
}
HTTP/3(QUIC)를 사용하려면 NGINX와 OpenSSL 버전에 특별한 설정이 필요할 수 있어요.
3) Apache (아파치)에서 HTTP/2 활성화하기
LoadModule http2_module modules/mod_http2.so # HTTP/2 모듈을 불러와요
Protocols h2 http/1.1 # HTTP/2를 기본으로 사용하고, 없으면 HTTP/1.1을 사용해요
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
ProtocolsHonorOrder On
Protocols h2 h2c http/1.1
</VirtualHost>
mod_http2 모듈을 로드하고 Protocols 지시자로 우선순위를 정해줘요.
4) HTTP/3 지원 고려사항
Apache에서 HTTP/3는 아직 개발 중이어서 완벽하게 지원되진 않아요. 만약 HTTP/3를 웹사이트에 적용하고 싶다면, Cloudflare, Fastly, Google Cloud CDN 같은 CDN 서비스를 이용하는 것이 훨씬 쉽고 안정적인 방법이에요. 이런 서비스들이 대신 HTTP/3를 적용해 줄 거예요.
웹사이트의 보안을 강화하기 위해 웹 브라우저에게 특별한 지시를 내리는 것이 바로 보안 관련 HTTP 헤더예요. 이것들을 설정하면 여러 가지 웹 공격을 막을 수 있어요.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadmax-age=31536000: 1년(3153만 6천 초) 동안 이 규칙을 기억하라는 뜻이에요.includeSubDomains: 이 도메인의 모든 하위 도메인(예: https://www.google.com/search?q=blog.example.com)에도 적용하라는 뜻이에요.preload: 이 사이트가 HSTS를 사용한다는 정보를 미리 웹 브라우저에 등록하도록 요청하는 거예요.X-Frame-Options: DENY # 아예 다른 사이트에 삽입될 수 없게 해요
# 또는
X-Frame-Options: SAMEORIGIN # 같은 도메인 내에서만 삽입을 허용해요X-Content-Type-Options: nosniffnosniff: '절대 멋대로 판단하지 말고, 서버가 보낸 Content-Type 헤더를 그대로 믿어라!'는 뜻이에요.Referrer-Policy: strict-origin-when-cross-originstrict-origin-when-cross-origin: 다른 출처(도메인)로 이동할 때는 전체 URL 대신 https://example.com처럼 도메인만 보내주고, 같은 출처로 이동할 때는 전체 URL을 보내줘요.Permissions-Policy: camera=(), geolocation=(self)camera=(): 이 웹사이트에서는 카메라를 사용할 수 없도록 해요.geolocation=(self): 이 웹사이트 자체(self)에서만 위치 정보(geolocation)를 사용할 수 있도록 해요. 다른 외부 스크립트가 마음대로 위치 정보를 가져가는 것을 막아요.Content-Security-Policy: default-src 'self'; img-src 'self' https://cdn.example.com; script-src 'self';default-src 'self': 기본적으로 모든 자원은 '우리 웹사이트(self)'에서만 불러올 수 있도록 해요.img-src 'self' https://cdn.example.com: 이미지는 우리 웹사이트와 https://cdn.example.com이라는 CDN에서만 불러올 수 있도록 해요.script-src 'self': 스크립트는 우리 웹사이트에서만 불러올 수 있도록 해요.데이터는 두 가지 주요 시점에서 암호화가 필요해요. 하나는 데이터를 주고받을 때이고, 다른 하나는 데이터를 저장해 둘 때예요.
Tip: 비밀번호는 절대로 원래 모습 그대로(평문) 저장하면 안 돼요!
bcrypt,Argon2처럼 강력한 해시 함수를 사용해서 비밀번호를 암호화된 형태로 저장해야 해요. 이렇게 하면 만약 데이터베이스가 털려도 사용자 비밀번호가 바로 유출되지 않아요.
Node.js의 Express.js를 사용한다면, 다음과 같이 미들웨어를 통해 보안 헤더를 한 번에 설정할 수 있어요.
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader('Permissions-Policy', 'camera=(), geolocation=(self)');
// Content-Security-Policy는 내용이 길어져 별도의 모듈을 사용하는 것이 좋아요.
next();
});
많은 사람이 SSL과 TLS를 섞어 쓰는 이유가 뭘까요? 그리고 이 둘은 정확히 어떻게 다른 걸까요?
역사적 배경
주요 차이점
GCM, ChaCha20-Poly1305처럼 강력하고 안전한 암호화 방식만 사용하도록 강제해요.0-RTT 모드를 기본으로 지원해요.실무에서 적용하는 팁
TLSv1.2와 TLSv1.3만 허용해야 해요.기술적으로는 'TLS'가 맞는 표현인데 왜 아직도 'SSL'이라는 말을 많이 쓸까요? 몇 가지 이유가 있어요.
정리: '프로토콜 자체'를 이야기할 때는 TLS라는 용어를 쓰는 것이 정확해요. 하지만 '인증서'를 이야기할 때는 오랜 관습 때문에 SSL 인증서라는 표현도 많이 쓰인다는 점을 이해하시면 됩니다. 중요한 건 용어보다는 실제로 TLS 1.2 이상의 최신 버전을 적용하는 것이라는 점을 기억해주세요!