HTTPS는 HTTP 요청을 SSL 혹은 TLS라는 알고리즘을 이용해 HTTP 통신을 하는 과정에서 내용을 암호화하여 데이터를 전송하는 방법입니다.
HTTPS를 사용하는 이유는 안전하고, 데이터 제공자의 신원을 보장받을 수 있기 때문입니다.
클라이언트는 데이터 제공자가 제공해 준 데이터를 사용할 수 밖에 없습니다.
중간자 공격을 방어할 수 있습니다.
https 프로토콜의 특징 중 하나는 암호화된 데이터를 주고받기 때문에, 중간에 인터넷 요청이 탈취되더라도 그 내용을 알아볼 수 없습니다.
또한 http 프로토콜보다 유출 가능성이 적습니다.
(데이터 알아 볼 수 없다.)
브라우저가 응답과 함께 전달된 인증서 정보를 확인할 수 있습니다.
브라우저는 인증서에서 해당 인증서를 발급한 CA 정보를 확인하고 인증된 CA가 발급한 인증서가 아니라면 경고창을 띄워 서버와 연결이 안전하지 않다는 화면을 보여줍니다.
경고를 직접 보여줌으로서, 데이터를 제공하는 안전한 서버를 사용할 수 있게 사용자를 유도합니다.
일종의 사이버 공격으로 통신 사이에 공격자를 삽입해, 메시지를 가로채고, 변경된 데이터를 전달합니다.
인증서를 생성하고, 인증서를 이용해 HTTPS 서버를 만들 수 있습니다.
일반적으로 HTTPS 프로토콜을 사용하고, HTTPS 프로토콜은 인증의 중요한 부분을 차지하기 때문에, 앞으로는 HTTPS 통신으로 진행합니다.
Homebrew를 통해 설치할 수 있습니다.
brew install mkcert
// 상황에 따라 firefox를 사용할 경우 설치가 필요할 수도 있습니다.
brew install nss
mkcert -install
상단 명령어를 입력 후, 로컬 환경에 대한 인증서를 만들어야 합니다.
localhost로 대표되는 로컬 환경에 대한 인증서를 만들기 위해 아래 명령어를 입력합니다.
mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1
localhost, 127.0.0.1(IPv4), ::1(IPv6) 에서 사용할 수 있는 인증서가 완성됐습니다.
완성 후 cert.pem, key.pem 이라는 파일이 생성된 것을 확인할 수 있습니다.
인증서를 통해 발급받은 key와 cert는 쿠키, 세션, 토큰에서 계속 활용하게 됩니다. 따라서 저장 경로를 반드시 확인해야 합니다.
인증서는 공개키, 인증기관의 서명을 포함하고 있으므로 공개되어도 상관없지만, key.pem의 경우 개인 key이므로 git에 커밋하면 않됩니다.
Node.js 환경에서 HTTPS 서버를 작성하기 위해서는 https 내장 모듈을 이용할 수 있습니다.
(express.js를 이용해 https 서버를 만들 수도 있습니다.)
ex) Node.js https 모듈 이용
const https = require('https');
const fs = require('fs');
https
.createServer(
{
key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
},
function(req, res) {
res.write("HTTPS Server complete");
res.end();
}
)
.lesten(3001);
서버 실행 후 https://localhost:3001 로 접속하면 url 창 좌측에 자물쇠로 잠겨있는 HTTPS 사용을 알 수 있습니다.
https.createServer 는 같지만 두 번째 파라미터에 들어갈 callback 함수를 express Middleware로 교체하면 끝입니다.
const https = requires('https');
const fs = require('fs');
const express = require('express');
const app = express();
https
.createServer(
{
key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
},
app.use('/', (req, res) => {
res.send('Server Working');
})
)
.lesten(3001);
쿠키는 서버에서 클라이언트에 데이터를 저장하는 방법 중 하나입니다.
서버가 원한다면 서버는 클라이언트에서 쿠키를 이용하여 데이터를 가져올 수 있습니다.
(쿠키를 이용하는 것은 단순히 서버에서 클라이언트에 쿠키를 전송하는 것만 의미하지 않고 클라이언트에서 서버로 쿠키를 전송하는 것도 포함됩니다.)
서버에 접속할 수 있는 이름입니다.
쿠키 옵션에서 도메인은 포트 및 서브 도메인 정보, 세부 경로를 포함하지 않습니다.
도메인 정보가 존재한다면 클라이언트에서는 쿠키의 도메인 옵션과 서버의 도메인이 일치해야만 쿠키를 전송할 수 있습니다.
세부 경로는 서버가 라우팅할 때 사용하는 경로입니다.
ex) /users/login
https://www.localhost.com:3000/users/login
세부 경로(path)는 /users/login 이 됩니다.
path를 만족하는 경우 쿠키 전송이 가능합니다. (전송되는 요청이 Path 옵션을 만족하지 못하는 경우 서버로 쿠키를 전송할 수 없습니다.)
쿠키가 유요한 시간을 설정하는 옵션입니다.
MaxAge와 Expires는 비슷합니다. 하지만 유요 Date를 지정하는 것이 Expires, 일정 시간 (ex. 100일, 3600초, 등등)이 MaxAge 입니다.
두 옵션이 지정된 경우가 아니면 브라우저 탭을 닫을 때 쿠키가 제거될 수 있습니다.
쿠키를 전송해야 할 때 사용하는 프로토콜에 따른 쿠키 전송 여부를 결정합니다.
해당 옵션이 true 설정된 경우, HTTPS 프로토콜을 이용하여 통신하는 경우에만 쿠키를 전송할 수 있습니다.
해당 옵션이 true로 설정된 경우, JS에서는 쿠키 접근이 불가능합니다.
HttpOnly 옵션이 false인 경우, JS에서 쿠키 접근이 가능합니다.
(XSS 공격에 취약해진다.)
Cross-Origin 요청을 받은 경우 요청에서 사용한 메소드와 해당 옵션의 조합으로 서버의 쿠키 전송 여부를 결정하게 됩니다.
Lax: Cross-origin 요청이면 'GET' 메소드에 대해서 쿠키를 전송할 수 있습니다.
Strict: Cross-origin이 아닌 Same-Site인 경우에만 쿠키를 전송할 수 있습니다.
None: 항상 쿠키를 보내줄 수 있습니다. 다만 쿠키 옵션 중 Secure 옵션이 필요합니다. 'Same-Site'는 요청을 보낸 Origin과 서버의 도메인이 같은 경우를 말합니다.
이러한 옵션들을 지정한 다음 서버에서 클라이언트로 쿠키를 처음 전송하게 된다면 헤더에 Set-Cookie라는 Property에 쿠키를 담아 쿠키를 전송하게 됩니다.
쿠키의 특성을 이용해 서버는 클라이언트에 인증정보를 담은 쿠키를 전송하고, 클라이언트는 전달받은 쿠키를 요청, 전송하여 Stateless한 인터넷 연결을 Stateful하게 유지할 수 있습니다.
쿠키에 민감한 정보를 담는 것은 위험합니다.