HTTP 쿠키는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각이다. 브라우저는 그 데이터 조각들을 저장해 놓았다가, 동일한 서버에 재요청 시 저장된 데이터를 함께 전송한다. 쿠키는 두 요청이 동일한 브라우저에서 들어왔는지 아닌지를 판단할 때 주로 사용된다. 이를 이용하면 사용자의 로그인 상태를 유지할 수 있게된다.
간단하게 쿠키는 아래와 같이 설정할 수 있다.
Set-Cookie: <cookie-name>=<cookie-value>
이 서버 헤더는 클라이언트에게 쿠키를 저장하라고 전달한다.
간단한 예제를 살펴보자.
const http = require("http");
http
.createServer((request, response) => {
response.writeHead(200, {
"Set-Cookie": ["yummy_cookie=choco", "tasty_cookie=strawberry"]
});
response.end("Cookie");
})
.listen(3000);
이렇게 쿠키를 생성해주면, Network 탭에서
Response Headers에 Set-Cookie라는 두개의 헤더값이 추가된 것을 확인할 수 있다.
이제, 쿠키 헤더를 생성하는 코드를 주석처리하고 다시 브라우에 접속해보자.
Response-Headers에는 쿠키를 더이상 보내지 않음에도 불구하고,
Request-Headers 요청값에 쿠키가 심어져있는것을 확인할 수 있다.
웹 브라우저는 이제부터 저장된 쿠키값을 이 Cookie
라는 헤더값을 통해 서버로 전송하게 된다.
반대로 이제 쿠키의 값을 읽어와보자.
위와 같이 쿠키의 값이 여러개 존재할 수 있으므로 우리는 파싱해서 다뤄야한다. 이때 cookie
라는 npm 패키지를 활용할 수 있다.
$ npm install -s cookie
const http = require("http");
const cookie = require("cookie");
http
.createServer((request, response) => {
let cookies = {};
if (request.headers.cookie !== undefined) { //쿠키가 존재할때(쿠키 읽기)
cookies = cookie.parse(request.headers.cookie);
}
console.log(cookies); // { yummy_cookie: 'choco', tasty_cookie: 'strawberry' }
response.writeHead(200, { //쿠키 생성 코드
"Set-Cookie": ["yummy_cookie=choco", "tasty_cookie=strawberry"]
});
response.end("Cookie");
})
.listen(3000);
우리가 지금까지 작성한 코드는 세션쿠키를 생성하고 있다.
Session Cookie는 현재 세션이 끝날 때 삭제된다.
반면 Persistent Cookie는 Expires
속성에 명시된 날짜에 삭제되거나, Max-Age
속성에 명시된 기간 이후에 삭제된다.
response.writeHead(200, {
"Set-Cookie": [
"yummy_cookie=choco",
"tasty_cookie=strawberry",
`Permanent=cookies; Max-Age=${60 * 60 * 24 * 30}` // 30일 동안만 지속되도록
]
});
이와 같이 Max-Age
속성을 통해서 해당 쿠키가 얼마나 지속될 것인지 설정해줄 수 있다.
Secure 쿠키는 HTTPS 프로토콜 상에서 암호화된 요청일 경우에만 전송된다.
HttpOnly 쿠키는 Cross-site 스크립팅(XSS) 공격을 방지하기 위해 Javascript의 Document.cookie
API에 접근할 수 없도록 하는 것이다. 이들은 서버에 전송되기만 한다. 서버 쪽에서 지속되고 있는 세션의 쿠키는 Javascript를 사용할 필요가 없기 때문에 HttpOnly
플래그가 설정될 것이다.
response.writeHead(200, {
"Set-Cookie": [
"Secure=Secure; Secure",
"HttpOnly=HttpOnly; HttpOnly"
]
});
Domain
과 Path
디렉티브는 쿠키의 스코프를 정의한다.
Path는 특정 디렉토리에서만 쿠키가 활성화될 수 있도록 한다. 즉 Cookie 헤더를 전송하기 위해 요청되는 URL 내에 반드시 존재해야하는 URL 경로이다.
response.writeHead(200, {
"Set-Cookie": [
"Path=Path; Path=/cookie"
]
});
이렇게 /cookie
가 아닌 url에서는 해당 Path 쿠키
가 보이지 않는 것을 확인할 수 있다.
Domain은 쿠키가 전송되게 될 호스트들을 명시한다. 만약 명시되지 않는다면, 현재 문서 위치의 호스트 일부를 기본값으로 한다.
예를 들어, Domain=o2.org
가 설정되면 쿠키들은 test.o2.org
와 같은 서브도메인 상에 포함되게 된다.
response.writeHead(200, {
"Set-Cookie": [
"Domain=Domain; Domain=o2.org"
]
});
쿠키는 클라이언트 로컬 측에 저장되기 때문에 조작되거나 request에서 스니핑 당할 우려가 있어서 보안에 취약하다.
반면, 이어서 살펴볼 세션은 쿠키를 이용해서 세션 아이디(식별자)만 저장하고, 이를 이용하여 서버에서 처리하기 때문에 비교적 보안이 좋다.
세션은 쿠키를 기반으로 하고있지만, 웹 서버가 세션 아이디 파일을 만들어 서비스가 돌아가고 있는 서버에 저장한다.
이렇게 쿠키는 사용자를 식별하는 데에만 사용하고, 실제 데이터는 서버 측에 파일 또는 데이터베이스 형태로 저장되기 때문에 사용자 정보를 노출하지 않고 보다 안전하게 구현할 수 있다.
express-session : 세션 관리용 미들웨어
$ npm install -s express-session
const session = require("express-session");
const app = express();
//app.use() : 요청이 있을때마다 해당 미들웨어가 실행된다
app.use(
session({
// 세션 동작 옵션
secret: "abcdefggh", // 공개되지 않도록 관리할 것
resave: false,
saveUninitialized: true
})
);
app.get("/", function (req, res, next) {
console.log(req.session);
if (req.session.num === undefined) {
req.session.num = 1;
} else {
req.session.num += 1;
}
res.send(`Views : ${req.session.num}`);
});
app.listen(3000, function () {
console.log("3000!!");
});
express-session 미들웨어는 req 객체에 session이라는 프로퍼티를 만들어준다.
따라서 콘솔창에 req.session을 출력해보면,
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
이와 같이 출력된다.
또한 Application 탭의 Cookies를 확인해보면 아래와 같이 sessionId가 저장된 것을 확인할 수 있다.
이제 세션을 본격적으로 사용하게 되면, 유저 세션에 대한 정보를 서버 어딘가에 저장해놓아야한다. 이때 파일 형태로 저장할 수도, 데이터베이스 (ex. mysql)을 사용할 수도 있다.
session-file-store 을 사용하여 파일 형태로 저장할 수 있다.
$ npm install -s session-file-store
const session = require("express-session");
const FileStore = require("session-file-store")(session);
app.use(
session({
// 세션 동작 옵션
secret: secret.secretId,
resave: false,
saveUninitialized: true,
store: new FileStore()
})
);
이와 같이 코드를 실행하면, 프로젝트 내 sessions
라는 폴더가 생성되고 하위에 json 파일이 생성된다.
{"cookie":{"originalMaxAge":null,"expires":null,"httpOnly":true,"path":"/"},"num":6,"__lastAccess":1633450656116}
(로그인할때를 예로 세션 방식이 어떻게 동작하는지 살펴보자)
사용자가 로그인을 요청하고, 조건을 만족함에 따라 세션이 서버의 메모리 상에 저장된다. 고유의 sessionId를 기준으로 정보를 저장하게 된다.
서버에서 sessionId를 cookie에 담아서 브라우저로 전달한다.
브라우저는 모든 request에 cookie(sessionId)를 함께 전송한다.
서버는 브라우저가 보낸 sessionId를 키로 서버 메모리에서 사용자의 session 정보를 식별하고 이가 유효하다면 응답을 제공한다.
[References]