본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 5. · 76 Views
네트워크 보안과 HTTPS 완벽 가이드
인터넷에서 데이터를 안전하게 주고받는 방법을 알아봅니다. 암호화의 기초부터 HTTPS가 동작하는 원리까지, 웹 보안의 핵심을 쉽게 설명합니다.
목차
1. 암호화의 필요성
김개발 씨는 오늘 처음으로 로그인 기능을 만들었습니다. 테스트 삼아 아이디와 비밀번호를 입력하고 전송 버튼을 눌렀는데, 옆에서 지켜보던 박시니어 씨가 깜짝 놀라며 말했습니다.
"잠깐, 지금 비밀번호가 그대로 네트워크로 나가고 있어요!"
암호화는 데이터를 알아볼 수 없는 형태로 변환하는 것입니다. 마치 비밀 편지를 쓸 때 암호표를 사용하는 것과 같습니다.
누군가 중간에서 편지를 가로채더라도 암호표가 없으면 내용을 읽을 수 없습니다. 웹에서 주고받는 모든 민감한 정보는 반드시 암호화해야 합니다.
다음 코드를 살펴봅시다.
// 암호화 없이 전송하면 위험합니다
const loginData = {
username: "kimdev",
password: "mySecret123" // 이 비밀번호가 그대로 노출됩니다
};
// 네트워크를 통해 전송될 때
// 해커가 패킷을 가로채면 이렇게 보입니다:
// {"username":"kimdev","password":"mySecret123"}
// 암호화하면 이렇게 변합니다:
// "aGVsbG8gd29ybGQh..." (알아볼 수 없는 문자열)
김개발 씨는 입사 2개월 차 신입 개발자입니다. 회사에서 처음으로 맡은 업무는 간단한 로그인 페이지를 만드는 것이었습니다.
폼을 만들고, 서버로 데이터를 전송하는 코드를 작성했습니다. 모든 게 잘 동작하는 것 같았습니다.
그런데 박시니어 씨가 개발자 도구의 네트워크 탭을 열어 보여주었습니다. 김개발 씨는 깜짝 놀랐습니다.
방금 입력한 비밀번호가 그대로 화면에 보이고 있었던 것입니다. "인터넷에서 데이터가 오가는 과정을 생각해 보세요." 박시니어 씨가 설명을 시작했습니다.
우리가 웹사이트에 접속할 때 데이터는 수많은 중간 지점을 거칩니다. 집에서 출발한 데이터는 공유기를 지나고, 인터넷 서비스 제공업체(ISP)를 거치고, 여러 라우터를 통과하여 목적지 서버에 도착합니다.
마치 편지가 우체국을 여러 번 거쳐 배달되는 것과 같습니다. 문제는 이 중간 지점 어디에서든 누군가가 데이터를 들여다볼 수 있다는 것입니다.
특히 카페나 공항 같은 공용 와이파이를 사용할 때는 더욱 위험합니다. 같은 네트워크에 있는 악의적인 사용자가 오가는 데이터를 모두 볼 수 있기 때문입니다.
이것을 **중간자 공격(Man-in-the-Middle Attack)**이라고 부릅니다. 공격자가 통신 중간에 끼어들어 데이터를 훔쳐보거나, 심지어 변조할 수도 있습니다.
그렇다면 어떻게 해야 할까요? 바로 암호화가 답입니다.
암호화는 원본 데이터를 특별한 알고리즘을 사용해서 알아볼 수 없는 형태로 바꾸는 것입니다. 예를 들어 "mySecret123"이라는 비밀번호가 "aGVsbG8gd29ybGQh"처럼 변환됩니다.
중간에서 누가 가로채더라도 원래 내용이 무엇인지 알 수 없습니다. 물론 수신자는 원래 내용을 알아야 합니다.
그래서 암호화된 데이터를 다시 원래대로 되돌리는 복호화 과정이 필요합니다. 이 복호화는 특별한 **키(Key)**가 있어야만 가능합니다.
비유하자면 자물쇠와 열쇠의 관계와 같습니다. 소중한 물건을 자물쇠로 잠그면 열쇠가 있는 사람만 열 수 있습니다.
암호화도 마찬가지입니다. 올바른 키가 없으면 암호화된 데이터를 풀 수 없습니다.
김개발 씨가 고개를 끄덕였습니다. "그러면 모든 통신을 암호화하면 되겠네요?" 박시니어 씨가 미소를 지었습니다.
"맞아요. 그리고 웹에서는 그것을 위해 HTTPS라는 것을 사용합니다.
오늘 하나씩 배워봅시다."
실전 팁
💡 - 공용 와이파이에서는 반드시 HTTPS 사이트만 이용하세요
- 개발 중에도 민감한 데이터는 로그에 남기지 않도록 주의하세요
- 브라우저 주소창의 자물쇠 아이콘으로 HTTPS 적용 여부를 확인할 수 있습니다
2. 대칭 키 암호화 방식
박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다. "암호화에는 크게 두 가지 방식이 있어요.
먼저 대칭 키 암호화부터 알아볼까요?" 김개발 씨는 펜을 들고 메모할 준비를 했습니다.
대칭 키 암호화는 암호화와 복호화에 같은 키를 사용하는 방식입니다. 마치 집 현관문 열쇠처럼, 잠글 때와 열 때 같은 열쇠를 사용합니다.
속도가 빠르다는 장점이 있지만, 키를 안전하게 전달하는 것이 어렵다는 문제가 있습니다.
다음 코드를 살펴봅시다.
const crypto = require('crypto');
// 대칭 키 생성 (양쪽이 같은 키를 사용)
const secretKey = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
// 암호화 함수
function encrypt(text) {
const cipher = crypto.createCipheriv('aes-256-cbc', secretKey, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
// 복호화 함수 (같은 키로 복호화)
function decrypt(encryptedText) {
const decipher = crypto.createDecipheriv('aes-256-cbc', secretKey, iv);
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
"가장 직관적인 암호화 방식부터 시작해 봅시다." 박시니어 씨가 말했습니다. 어릴 때 친구와 비밀 편지를 주고받아 본 적이 있으신가요?
"ㄱ을 ㄴ으로, ㄴ을 ㄷ으로 바꾸자"처럼 약속을 정해두면, 그 약속을 아는 사람만 편지를 읽을 수 있습니다. 이것이 바로 대칭 키 암호화의 기본 원리입니다.
대칭 키 암호화에서는 암호화할 때 사용하는 키와 복호화할 때 사용하는 키가 동일합니다. 송신자와 수신자가 미리 같은 키를 공유하고 있어야 합니다.
위의 코드를 살펴보겠습니다. secretKey라는 32바이트 키를 생성했습니다.
이 키로 encrypt 함수를 사용해 평문을 암호화하고, 같은 키로 decrypt 함수를 사용해 복호화합니다. AES-256은 현재 가장 널리 쓰이는 대칭 키 암호화 알고리즘입니다.
대칭 키 암호화의 가장 큰 장점은 속도입니다. 계산이 단순하기 때문에 대용량 데이터도 빠르게 암호화할 수 있습니다.
동영상 스트리밍이나 파일 전송처럼 많은 양의 데이터를 처리해야 할 때 주로 사용됩니다. "그런데 문제가 하나 있어요." 박시니어 씨가 말을 이었습니다.
바로 키 배포 문제입니다. 송신자와 수신자가 같은 키를 가지고 있어야 하는데, 이 키를 어떻게 안전하게 전달할 수 있을까요?
키를 네트워크로 전송하면 중간에서 가로챌 수 있습니다. 직접 만나서 전달하자니 인터넷 시대에 그럴 수는 없습니다.
전 세계 수억 개의 웹사이트와 일일이 키를 교환하는 것은 불가능합니다. 김개발 씨가 고개를 갸웃거렸습니다.
"그러면 어떻게 해야 하나요?" "바로 그 문제를 해결하기 위해 공개 키 암호화라는 것이 등장했어요." 박시니어 씨가 다음 주제를 예고했습니다. 정리하자면, 대칭 키 암호화는 빠르고 효율적이지만 키 공유가 어렵습니다.
그래서 실제 HTTPS에서는 대칭 키와 공개 키 암호화를 함께 사용합니다. 처음에 공개 키 암호화로 안전하게 대칭 키를 교환하고, 이후 데이터 전송은 빠른 대칭 키 암호화로 처리하는 것입니다.
실전 팁
💡 - AES-256은 현재 가장 안전한 대칭 키 알고리즘 중 하나입니다
- 대칭 키는 절대 코드에 하드코딩하지 말고 환경 변수로 관리하세요
- 실제 서비스에서는 IV(초기화 벡터)를 매번 새로 생성해야 합니다
3. 공개 키 비대칭 암호화 방식
"대칭 키의 문제점을 해결할 방법이 있습니다." 박시니어 씨가 화이트보드에 두 개의 열쇠를 그렸습니다. 하나는 자물쇠처럼 생겼고, 하나는 열쇠처럼 생겼습니다.
김개발 씨는 궁금한 눈으로 바라보았습니다.
공개 키 암호화는 서로 다른 두 개의 키를 사용합니다. 하나는 누구에게나 공개하는 공개 키, 다른 하나는 본인만 가지는 개인 키입니다.
공개 키로 암호화한 것은 개인 키로만 풀 수 있습니다. 이 방식을 사용하면 키 배포 문제가 해결됩니다.
다음 코드를 살펴봅시다.
const crypto = require('crypto');
// 공개 키와 개인 키 쌍 생성
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
});
// 공개 키로 암호화 (누구나 할 수 있음)
function encryptWithPublicKey(text) {
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(text));
return encrypted.toString('base64');
}
// 개인 키로 복호화 (키 소유자만 가능)
function decryptWithPrivateKey(encryptedText) {
const buffer = Buffer.from(encryptedText, 'base64');
const decrypted = crypto.privateDecrypt(privateKey, buffer);
return decrypted.toString('utf8');
}
1976년, 휘트필드 디피와 마틴 헬먼이라는 두 학자가 혁명적인 아이디어를 발표했습니다. 바로 공개 키 암호화 개념입니다.
이것은 암호학 역사에서 가장 중요한 발명 중 하나로 꼽힙니다. "비유를 들어볼게요." 박시니어 씨가 설명을 시작했습니다.
여러분이 편지함을 가지고 있다고 생각해 보세요. 이 편지함은 특별합니다.
누구나 편지를 넣을 수 있는 투입구가 있지만, 편지를 꺼내는 것은 열쇠를 가진 주인만 할 수 있습니다. 투입구가 공개 키이고, 열쇠가 개인 키입니다.
공개 키는 말 그대로 공개해도 됩니다. 온 세상에 알려도 상관없습니다.
누구나 이 공개 키를 사용해서 메시지를 암호화할 수 있습니다. 하지만 암호화된 메시지를 풀 수 있는 것은 개인 키를 가진 사람뿐입니다.
이 방식의 마법 같은 점은 공개 키로 암호화한 것은 공개 키로 풀 수 없다는 것입니다. 오직 짝이 되는 개인 키로만 복호화됩니다.
수학적으로 두 키는 연결되어 있지만, 공개 키에서 개인 키를 계산해내는 것은 현실적으로 불가능합니다. 김개발 씨가 질문했습니다.
"그러면 키 배포 문제가 해결되는 건가요?" "맞아요!" 박시니어 씨가 답했습니다. 웹사이트가 공개 키를 모두에게 알려줍니다.
김개발 씨의 브라우저가 이 공개 키로 비밀 메시지를 암호화해서 보냅니다. 중간에 해커가 가로채도 걱정 없습니다.
공개 키로는 복호화할 수 없으니까요. 오직 웹사이트만이 자신의 개인 키로 메시지를 열어볼 수 있습니다.
하지만 공개 키 암호화에도 단점이 있습니다. 바로 속도입니다.
복잡한 수학 연산이 필요하기 때문에 대칭 키 암호화보다 훨씬 느립니다. 대용량 데이터를 모두 공개 키 방식으로 암호화하면 성능이 크게 떨어집니다.
그래서 실제로는 하이브리드 방식을 사용합니다. 처음에 공개 키 암호화로 대칭 키를 안전하게 교환합니다.
그 이후의 모든 데이터는 빠른 대칭 키 암호화로 주고받습니다. 두 방식의 장점만 취하는 것입니다.
위의 코드를 보면 generateKeyPairSync로 RSA 키 쌍을 생성합니다. RSA는 가장 널리 쓰이는 공개 키 알고리즘입니다.
publicEncrypt로 암호화하고 privateDecrypt로 복호화하는 것을 볼 수 있습니다.
실전 팁
💡 - RSA 키는 최소 2048비트 이상을 사용하세요
- 개인 키는 절대 외부에 노출되면 안 됩니다
- 공개 키 암호화는 키 교환이나 작은 데이터에만 사용하세요
4. 인증서와 CA
김개발 씨가 한 가지 의문을 제기했습니다. "잠깐요, 웹사이트가 공개 키를 보내준다고 했는데, 그 공개 키가 진짜 그 웹사이트의 것인지 어떻게 알 수 있나요?" 박시니어 씨가 빙그레 웃었습니다.
"좋은 질문이에요!"
인증서는 공개 키의 소유자가 누구인지 증명하는 전자 문서입니다. **CA(Certificate Authority)**는 이 인증서를 발급하는 신뢰할 수 있는 기관입니다.
마치 정부가 발급하는 신분증처럼, CA가 발급한 인증서는 해당 공개 키가 진짜 그 웹사이트의 것임을 보증합니다.
다음 코드를 살펴봅시다.
// Node.js로 인증서 정보 확인하기
const https = require('https');
// 웹사이트의 인증서 정보 가져오기
const options = {
hostname: 'www.google.com',
port: 443,
method: 'GET'
};
const req = https.request(options, (res) => {
const cert = res.socket.getPeerCertificate();
console.log('발급 대상:', cert.subject.CN);
console.log('발급 기관:', cert.issuer.CN);
console.log('유효 기간:', cert.valid_from, '~', cert.valid_to);
});
req.end();
박시니어 씨가 비유를 들었습니다. "해외여행을 가려면 여권이 필요하죠?" 여권은 정부가 발급하는 신분증입니다.
다른 나라 입국 심사대에서 여권을 보여주면, 그 사람이 누구인지 신뢰할 수 있습니다. 왜냐하면 정부라는 신뢰할 수 있는 기관이 보증하기 때문입니다.
인터넷에서도 마찬가지입니다. 웹사이트가 "나는 네이버야, 여기 내 공개 키야"라고 말해도 그것만으로는 믿을 수 없습니다.
해커가 가짜 네이버를 만들고 자기 공개 키를 보내줄 수도 있으니까요. 이것을 피싱(Phishing) 공격이라고 합니다.
이 문제를 해결하기 위해 **CA(Certificate Authority)**라는 기관이 있습니다. DigiCert, Let's Encrypt, Comodo 같은 회사들이 대표적인 CA입니다.
CA는 웹사이트의 신원을 확인한 후 인증서를 발급합니다. 인증서에는 다음 정보가 포함됩니다.
웹사이트 도메인 이름, 웹사이트의 공개 키, 인증서 유효 기간, 발급한 CA 정보, 그리고 CA의 디지털 서명이 들어있습니다. 브라우저는 웹사이트에 접속할 때 이 인증서를 받습니다.
그리고 다음을 확인합니다. 인증서가 신뢰할 수 있는 CA에서 발급되었는지, 인증서의 도메인과 접속한 도메인이 일치하는지, 인증서가 아직 유효한지를 검사합니다.
"브라우저가 어떻게 CA를 신뢰하나요?" 김개발 씨가 물었습니다. 브라우저와 운영체제에는 신뢰할 수 있는 CA 목록이 미리 내장되어 있습니다.
이 목록에 있는 CA가 발급한 인증서만 신뢰합니다. 만약 알 수 없는 기관이 발급한 인증서라면 브라우저는 경고 메시지를 보여줍니다.
위의 코드는 실제로 웹사이트의 인증서 정보를 확인하는 예제입니다. getPeerCertificate() 메서드로 인증서 정보를 가져올 수 있습니다.
발급 대상, 발급 기관, 유효 기간 등을 확인할 수 있습니다. 인증서가 없거나 유효하지 않으면 브라우저는 "이 연결은 안전하지 않습니다"라는 경고를 표시합니다.
이런 경고를 무시하고 접속하면 개인정보가 유출될 수 있으니 주의해야 합니다.
실전 팁
💡 - 브라우저 경고가 뜨는 사이트에서는 민감한 정보를 입력하지 마세요
- 개발용 자체 서명 인증서는 로컬에서만 사용하세요
- Let's Encrypt를 이용하면 무료로 인증서를 발급받을 수 있습니다
5. 디지털 서명의 원리
"인증서에 CA의 디지털 서명이 포함된다고 했는데, 디지털 서명이 뭔가요?" 김개발 씨가 질문했습니다. 박시니어 씨가 고개를 끄덕였습니다.
"좋아요, 이건 공개 키 암호화를 거꾸로 사용하는 거예요."
디지털 서명은 문서의 작성자와 무결성을 증명하는 기술입니다. 개인 키로 서명을 만들고, 공개 키로 서명을 검증합니다.
마치 계약서에 서명하는 것처럼, 디지털 서명이 있으면 누가 작성했는지 그리고 내용이 변조되지 않았는지 확인할 수 있습니다.
다음 코드를 살펴봅시다.
const crypto = require('crypto');
// 키 쌍 생성
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
});
// 개인 키로 서명 생성
function createSignature(message) {
const sign = crypto.createSign('SHA256');
sign.update(message);
return sign.sign(privateKey, 'base64');
}
// 공개 키로 서명 검증
function verifySignature(message, signature) {
const verify = crypto.createVerify('SHA256');
verify.update(message);
return verify.verify(publicKey, signature, 'base64');
}
// 사용 예시
const message = '이 계약에 동의합니다';
const signature = createSignature(message);
console.log('서명 유효:', verifySignature(message, signature));
박시니어 씨가 설명을 시작했습니다. "공개 키 암호화를 기억하죠?
공개 키로 암호화하고 개인 키로 복호화했어요. 디지털 서명은 그 반대입니다." 디지털 서명에서는 개인 키로 서명을 만들고, 공개 키로 검증합니다.
개인 키는 본인만 가지고 있으니까, 그 개인 키로 만든 서명은 본인이 만든 것이라고 증명할 수 있습니다. 실제 종이 계약서에 서명하는 것을 생각해 보세요.
본인만의 고유한 필체로 서명합니다. 나중에 누군가 "이 계약서 당신이 서명한 거 맞아요?"라고 물으면, 필적 감정으로 확인할 수 있습니다.
디지털 서명도 마찬가지입니다. 개인 키로 만든 서명은 그 개인 키의 소유자만이 만들 수 있습니다.
공개 키로 검증하면 정말 그 사람이 서명했는지 확인할 수 있습니다. 여기서 중요한 점이 하나 더 있습니다.
바로 무결성 검증입니다. 서명을 만들 때, 원본 메시지 전체를 암호화하지 않습니다.
대신 메시지의 해시값을 계산하고, 그 해시값에 서명합니다. 해시는 메시지의 지문 같은 것입니다.
메시지가 조금이라도 바뀌면 해시값이 완전히 달라집니다. 검증할 때는 메시지의 해시를 다시 계산하고, 서명을 복호화해서 나온 해시와 비교합니다.
둘이 같으면 메시지가 변조되지 않은 것입니다. "아, 그래서 인증서 위조가 어려운 거군요!" 김개발 씨가 이해했습니다.
맞습니다. CA는 인증서 내용에 자신의 개인 키로 서명합니다.
누군가 인증서 내용을 바꾸면 서명 검증이 실패합니다. CA의 개인 키 없이는 유효한 서명을 만들 수 없으니까요.
위의 코드를 보면 createSign으로 서명 객체를 만들고 sign.sign(privateKey)로 개인 키를 사용해 서명합니다. 검증할 때는 createVerify를 사용하고 verify.verify(publicKey)로 공개 키를 사용합니다.
디지털 서명은 인증서뿐 아니라 소프트웨어 배포, 이메일 보안, 블록체인 등 다양한 곳에서 사용됩니다.
실전 팁
💡 - 디지털 서명과 암호화는 다릅니다. 서명은 인증과 무결성, 암호화는 기밀성을 제공합니다
- SHA-256 이상의 해시 알고리즘을 사용하세요
- Git 커밋에도 디지털 서명을 적용할 수 있습니다 (GPG 서명)
6. SSL TLS 핸드셰이크
"이제 모든 퍼즐 조각이 모였어요." 박시니어 씨가 말했습니다. "대칭 키, 공개 키, 인증서, 디지털 서명.
이것들이 어떻게 조합되는지 볼까요?" 김개발 씨는 드디어 전체 그림을 볼 수 있게 되어 기대가 되었습니다.
SSL/TLS 핸드셰이크는 브라우저와 서버가 안전한 통신을 시작하기 전에 거치는 협상 과정입니다. 이 과정에서 서버 인증, 암호화 방식 합의, 세션 키 교환이 이루어집니다.
마치 두 사람이 비밀 대화를 나누기 전에 암호표를 정하는 것과 같습니다.
다음 코드를 살펴봅시다.
// TLS 핸드셰이크 과정을 의사 코드로 표현
// 실제로는 브라우저와 서버가 자동으로 처리합니다
// 1단계: 클라이언트 Hello
const clientHello = {
tlsVersion: 'TLS 1.3',
supportedCiphers: ['AES-256-GCM', 'CHACHA20'],
clientRandom: generateRandomBytes(32)
};
// 2단계: 서버 Hello + 인증서
const serverHello = {
selectedCipher: 'AES-256-GCM',
serverRandom: generateRandomBytes(32),
certificate: serverCertificate
};
// 3단계: 키 교환 (Pre-Master Secret)
const preMasterSecret = generatePreMasterSecret();
const encryptedSecret = encryptWithServerPublicKey(preMasterSecret);
// 4단계: 세션 키 생성 (양쪽이 동일한 키를 계산)
const sessionKey = deriveKey(preMasterSecret, clientRandom, serverRandom);
박시니어 씨가 화이트보드에 클라이언트와 서버 사이의 화살표를 그리기 시작했습니다. "비유를 들어볼게요.
영화에서 스파이들이 처음 만나면 어떻게 하죠?" 서로 신원을 확인하고, 암호를 정하고, 그 다음부터 비밀 대화를 나눕니다. TLS 핸드셰이크도 정확히 이 과정입니다.
1단계: Client Hello 브라우저가 먼저 인사를 건넵니다. "안녕하세요, 저는 TLS 1.3을 지원하고, AES-256이나 CHACHA20 암호화를 할 수 있어요." 그리고 랜덤한 숫자를 하나 보냅니다.
이것을 Client Random이라고 합니다. 2단계: Server Hello 서버가 응답합니다.
"네, AES-256으로 하죠." 서버도 랜덤 숫자(Server Random)를 보내고, 자신의 인증서도 함께 보냅니다. 3단계: 인증서 검증 브라우저가 받은 인증서를 검사합니다.
신뢰할 수 있는 CA가 서명했는지, 도메인이 일치하는지, 유효 기간이 지나지 않았는지 확인합니다. 모두 통과하면 다음 단계로 넘어갑니다.
4단계: 키 교환 이제 양쪽이 같은 세션 키(대칭 키)를 만들어야 합니다. 브라우저는 Pre-Master Secret이라는 랜덤 값을 만듭니다.
이것을 서버의 공개 키로 암호화해서 보냅니다. 서버는 자신의 개인 키로 복호화해서 Pre-Master Secret을 얻습니다.
이제 양쪽 모두 Client Random, Server Random, Pre-Master Secret 세 가지를 가지고 있습니다. 5단계: 세션 키 생성 양쪽이 같은 공식으로 세 가지 값을 조합합니다.
그 결과로 동일한 세션 키가 만들어집니다. 이 세션 키는 네트워크로 전송된 적이 없기 때문에 안전합니다.
6단계: Finished 마지막으로 양쪽이 "준비 완료" 메시지를 교환합니다. 이 메시지는 방금 만든 세션 키로 암호화됩니다.
상대방이 정상적으로 복호화할 수 있다면 핸드셰이크 성공입니다. "와, 생각보다 복잡하네요." 김개발 씨가 말했습니다.
"맞아요. 하지만 이 모든 과정이 1초도 안 걸려요." 박시니어 씨가 덧붙였습니다.
핸드셰이크가 끝나면 이후의 모든 데이터는 세션 키로 암호화됩니다. 빠른 대칭 키 암호화를 사용하므로 성능 저하가 거의 없습니다.
실전 팁
💡 - TLS 1.3은 이전 버전보다 핸드셰이크 속도가 빨라졌습니다
- 서버 설정에서 오래된 TLS 버전(1.0, 1.1)은 비활성화하세요
- 핸드셰이크 실패 시 브라우저 개발자 도구의 보안 탭에서 원인을 확인할 수 있습니다
7. HTTPS 동작 원리
"자, 이제 모든 것을 종합해 봅시다." 박시니어 씨가 말했습니다. "HTTPS는 우리가 배운 모든 기술이 합쳐진 결과물이에요." 김개발 씨는 마침내 전체 그림이 그려지기 시작했습니다.
HTTPS는 HTTP에 TLS 보안 계층을 추가한 프로토콜입니다. 브라우저와 서버 사이의 모든 통신이 암호화되어 도청, 변조, 위장 공격으로부터 보호됩니다.
오늘날 대부분의 웹사이트가 HTTPS를 기본으로 사용합니다.
다음 코드를 살펴봅시다.
// Node.js HTTPS 서버 만들기
const https = require('https');
const fs = require('fs');
// 인증서와 개인 키 로드
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
// HTTPS 서버 생성
const server = https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('안전한 연결입니다!');
});
server.listen(443, () => {
console.log('HTTPS 서버가 443 포트에서 실행 중입니다');
});
// 클라이언트의 모든 요청은 자동으로 암호화됩니다
// 개발자가 추가로 할 일은 없습니다
드디어 마지막 퍼즐 조각입니다. 지금까지 배운 모든 것이 어떻게 연결되는지 정리해 봅시다.
HTTP는 웹에서 데이터를 주고받는 프로토콜입니다. 브라우저가 서버에 요청을 보내고 응답을 받는 규칙을 정의합니다.
하지만 HTTP는 평문 통신입니다. 누구나 중간에서 내용을 볼 수 있습니다.
HTTPS는 HTTP에 **S(Secure)**를 붙인 것입니다. S는 바로 TLS 보안 계층을 의미합니다.
HTTP와 TCP 사이에 TLS가 끼어들어 모든 데이터를 암호화합니다. 김개발 씨가 네이버에 접속하는 과정을 따라가 봅시다.
먼저 브라우저 주소창에 https://www.naver.com을 입력합니다. 브라우저는 네이버 서버의 443번 포트로 연결합니다.
443은 HTTPS의 기본 포트입니다. 연결이 되면 TLS 핸드셰이크가 시작됩니다.
앞서 배운 대로 Client Hello, Server Hello, 인증서 검증, 키 교환이 일어납니다. 이 과정은 밀리초 단위로 빠르게 진행됩니다.
핸드셰이크가 완료되면 브라우저와 네이버 서버는 같은 세션 키를 가지게 됩니다. 이제부터 모든 HTTP 요청과 응답은 이 세션 키로 암호화됩니다.
김개발 씨가 검색창에 "맛집"을 입력하고 검색 버튼을 누릅니다. 이 요청은 세션 키로 암호화되어 전송됩니다.
중간에 누가 가로채도 "aH3kD8sL..."처럼 알아볼 수 없는 데이터만 보입니다. 네이버 서버가 검색 결과를 응답합니다.
이 응답도 암호화됩니다. 브라우저가 세션 키로 복호화해서 화면에 결과를 보여줍니다.
HTTPS가 제공하는 보안은 세 가지입니다. 첫째, **기밀성(Confidentiality)**입니다.
데이터가 암호화되어 도청할 수 없습니다. 둘째, **무결성(Integrity)**입니다.
데이터가 중간에 변조되면 감지됩니다. TLS는 각 메시지에 MAC(Message Authentication Code)을 포함시켜 변조 여부를 확인합니다.
셋째, **인증(Authentication)**입니다. 인증서를 통해 서버가 진짜 네이버인지 확인합니다.
가짜 사이트에 속지 않습니다. "이제 로그인 기능을 어떻게 고쳐야 할지 알겠어요!" 김개발 씨가 말했습니다.
위의 코드처럼 HTTPS 서버를 설정하면 됩니다. 인증서와 개인 키 파일만 지정하면 나머지는 자동입니다.
개발자가 직접 암호화 코드를 작성할 필요가 없습니다. 오늘날 모든 웹사이트는 HTTPS를 사용해야 합니다.
구글은 HTTPS 사이트를 검색 순위에서 우대합니다. 브라우저들도 HTTP 사이트에 "안전하지 않음" 경고를 표시합니다.
HTTPS는 선택이 아닌 필수가 되었습니다.
실전 팁
💡 - 운영 환경에서는 반드시 신뢰할 수 있는 CA의 인증서를 사용하세요
- HTTP에서 HTTPS로 자동 리다이렉트를 설정하세요
- HSTS 헤더를 설정하면 브라우저가 항상 HTTPS로 접속합니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Grafana 대시보드 완벽 가이드
실시간 모니터링의 핵심, Grafana 대시보드를 처음부터 끝까지 배워봅니다. Prometheus 연동부터 알람 설정까지, 초급 개발자도 쉽게 따라할 수 있는 실전 가이드입니다.
분산 추적 완벽 가이드
마이크로서비스 환경에서 요청의 전체 흐름을 추적하는 분산 추적 시스템의 핵심 개념을 배웁니다. Trace, Span, Trace ID 전파, 샘플링 전략까지 실무에 필요한 모든 것을 다룹니다.
CloudFront CDN 완벽 가이드
AWS CloudFront를 활용한 콘텐츠 배포 최적화 방법을 실무 관점에서 다룹니다. 배포 생성부터 캐시 설정, HTTPS 적용까지 단계별로 알아봅니다.