-
[TIL] 스파르타) Node.js 숙련주차 강의 수강 - 2일 차 (Cookie, Session)TIL-sparta 2024. 5. 21. 19:04
> Node.js 숙련주차 강의를 수강하고 배운 내용들을 정리해보았다. JWT에 대한 내용까지 커버하려고 했으나 부트 캠프 우수 TIL로 선정(링크)되면서 Slack을 통해 TIL에 대한 피드백을 받아서 이전에 작성했던 TIL 내용 보강을 하게 됐고, 아무래도 여러 사람이 보게되어서 이전부터 신경쓰이던 TIL 서식 정리를 통한 가독성 개선 작업을 우선 진행했다. 강의 내용에서는 session을 추가로 알아보느라 시간을 많이 써서 JWT는 내일 작성할 TIL에 정리하기로 했다.
학습 키워드: Node.js, express, cookie, session
1. Cookie
1) What is it?:
Cookie: 웹 사이트에 접속할 때 서버로 부터 Set-Cookie header를 전달 받게 되면 브라우저에 저장되는 4KB 이하의 크기를 가지는 텍스트 파일이다. First-party cookie와 third-part cookie로 나뉜다. First-party 쿠키는 방문하고 있는 사이트에서 생성된 쿠키고, third-party cookie는 광고 등의 개인화를 위해 다른 사이트에서 생성되어 따라오는 쿠키를 뜻한다.
2) How does it work?:
// 'Set-Cookie'를 이용하여 쿠키를 할당하는 API app.get("/set-cookie", (req, res) => { let expire = new Date(); expire.setMinutes(expire.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다. res.writeHead(200, { 'Set-Cookie': `name=sparta; Expires=${expire.toGMTString()}; HttpOnly; Path=/`, }); return res.end(); });
// 'res.cookie()'를 이용하여 쿠키를 할당하는 API app.get("/set-cookie", (req, res) => { let expires = new Date(); expires.setMinutes(expires.getMinutes() + 60); // 만료 시간을 60분으로 설정합니다. res.cookie('name', 'sparta', { expires: expires }); return res.end(); });
강의 노트에서 발췌한 스크립트다. 먼저 첫 번째는 Node.js의 방식인데, res.writeHead를 이용하여 Set-Cookie를 헤더에 직접 정의해준다. 쿠키는 key-value 페어로 key=value 의 형식으로 묶여 있다. 위의 예제에서는 name이 key 값이고, sparta가 value가 되는 쿠키가 생성된다. 쿠키의 뒤에 Expires (만료 일자)나 Secure (HTTPS로 전송), HttpOnly (쿠키 접근 제한) 등 여러가지 옵션들을 세미콜론으로 구분하여 이어 붙여줄 수 있다.
두 번째 방식은 express에서 제공하는 방법이다. res.cookie로 쿠키의 key, value를 각각 첫 번째와 두 번째 인자로 받고, 세 번째 인자에서 object 안에 expires와 같은 옵션을 key-value 페어로 지정해 줄 수 있다.
3) Why use it?:
쿠키를 사용하면 이전 방문 정보를 기억하여 재활용할 수 있다. 예를 들면 로그인 정보나 personalization 설정 정보 및 각종 유저 analytics 정보 등을 기억했다가, 다음 번 방문에서 쿠키를 기반으로 여러 설정들(로그인 상태가 유지된다거나, 수집된 analytics 정보를 통해 적절한 광고가 나타나도록 하는 등)을 적용하도록 할 수 있다.
4) Why NOT use it?:쿠키를 단독으로 사용하는 경우 여러가지 문제점이 발생하는데, 정보가 클라이언트에서만 저장되기 때문에 위조 및 변조가 쉬워 보안이 취약해진다. 예를 들면, 로그인 정보 등을 쿠키만을 이용하여 관리하는 경우 로그인 쿠키가 탈취 당했을 때 서버에서는 탈취 사실 및 쿠키를 사용하여 정보를 요청하는 유저를 검증할 방법이 없기 때문에 로그인 후에만 접근 가능해야 하는 민감한 정보들이 그대로 노출되게 된다.
추가로, MDN 문서에 따르면 예전에는 클라이언트에서 정보를 저장할 때 따로 대안이 없어서 쿠키를 사용하여 많은 정보들을 보관했으나, 현재는 Modern storage API를 사용할 것을 권장하고 있다. 이전에 TMDB 프로젝트 때 사용한 localStorage와 sessionStorage가 Web Storage API로 여기에 속한다.
여담으로, 구글은 Chrome에서 third-party 쿠키를 없앨 예정이라고 한다. 유저의 프라이버시와 관련된 이른바 Privacy Sandbox 프로젝트의 일환인데, 2020년에 계획(Chromium 블로그 링크)되어 조금씩 딜레이 되다가 2024년 말 쯤에는 작업이 완료된다고 한다. 자세한 내용은 링크를 참고하자.
2. Session
1) What is it?:
앞서 언급했듯이 서버에서 쿠키를 발행하더라도 쿠키는 클라이언트에서만 보관되는 정보이기 때문에 요청에서 사용되는 쿠키가 이 서버에서 발행됐는지, 변조되지는 않았는지 등을 알 방법이 없다. 이러한 보안 문제를 해결하기 위해 고안된 방법이 바로 세션이다. 세션은 서버 측에서 암호화되어 메모리에 저장되어 관리되는 객체다.
2) How does it work?:
import express from 'express'; import cookieParser from 'cookie-parser'; const app = express(); const PORT = 5001; app.use(express.json()); app.use(cookieParser()); // 'req.cookies'를 이용하여 클라이언트의 모든 쿠키를 조회하는 API app.get('/get-cookie', (req, res) => { const cookies = req.cookies; console.log(cookies); return res.status(200).json({ cookie: cookies }); }); let session = {}; app.get('/set-session', function (req, res, next) { // 현재는 sparta라는 이름으로 저장하지만, 나중에는 복잡한 사용자의 정보로 변경될 수 있습니다. const name = 'sparta'; const uniqueInt = Date.now(); // 세션에 사용자의 시간 정보 저장 session[uniqueInt] = { name }; res.cookie('sessionKey', uniqueInt); return res.status(200).end(); }); app.get('/get-session', function (req, res, next) { const { sessionKey } = req.cookies; // 클라이언트의 쿠키에 저장된 세션키로 서버의 세션 정보를 조회합니다. const name = session[sessionKey]; return res.status(200).json({ name }); }); app.listen(PORT, () => { console.log(PORT, '포트로 서버가 열렸어요!'); });
위 스크립트는 강의에서 설명을 위해 간단하게 구현된 방법인데, session에 빈 객체를 선언해주고, 새 세션의 생성이 요청될 때 마다 세션 객체에 현재 시간을 key로 가지고, user의 이름을 값으로 가지는 항목을 추가한 뒤 res.cookie를 이용하여 sessionKey를 key로 가지고 세션에서의 key값을 value로 묶어 cookie를 돌려주는 방식이다.
단순하게 쿠키만 사용했을 때는 서버에서 상태를 저장하지 않기 때문에 알 수 없었던 정보들이, session 객체를 유지하게 되면서 교차 검증을 할 수 있게 되어 위조나 변조로부터 정보를 보호할 수 있게 되었다. 하지만 여전히 hijacking에 대한 대비가 미흡한데, 이는 ip 주소 등을 추가로 관리하여 세션 접속 위치에 변동이 생긴 경우 세션을 삭제하도록 조치하는 등의 방법으로 해결할 수 있다.
const express = require('express'); const session = require('express-session'); const app = express(); app.use(session({ secret: 'your_secret_key', // Replace with your secret key resave: false, saveUninitialized: true, cookie: { secure: false } // Set to `true` if you are using HTTPS }));
다음은 링크에서 가져온 express-session 패키지를 이용한 session 구현의 일부다. Session 객체에 옵션을 넣어 생성한 뒤 app.use로 미들웨어에 추가해주고 나면, 이후 req.session 을 통해 key-value 페어를 추가해주는 식으로 관리할 수 있다. 예를 들면, req.session.someKey = 1 이라는 라인을 작성하면, 세션 객체에 1 값을 가진 someKey라는 key 값이 추가 된다. 여기서 secret의 값이 해싱에 사용되는 키인데, 이를 .env 파일에 넣어서 관리하면 된다.
3) Why use it?:
HTTP 연결은 기본적으로 stateless 하다. 이는 서로 다른 요청 간의 연결고리가 없다는 뜻인데, 당장에 위에서 계속 언급한 로그인 검증 문제라던가, 나아가 세션을 설명할 때 항상 등장하는 웹 쇼핑의 장바구니의 내용물 정보 등을 유지할 수 없다는 문제가 생긴다. 하지만 session을 만들어 이용하게 되면 이러한 stateless 웹 서버의 약점들을 어느 정도 극복할 수 있게 된다.
단점으로는 아무래도 서버의 메모리에 모든 정보가 저장되기 때문에 유저수가 과도하게 많아지면 부하가 커져서 관리가 어려워진다.
세션이라는 개념은 알겠는데 동작에 관한 개념이 머릿속에서 명확하게 정리되지가 않아서 꽤 오랜 시간을 들여 찾아봤는데, 완벽하게 이해했다고는 말을 못하겠다. 추후 프로젝트에서 사용하게 될 때 실전 경험을 바탕으로 내용을 보강하도록 한다.
--
REFERENCES:
> 1주차 강의 노트, "쿠키와 세션"
> geeksforgeeks, "Difference between Session and Cookies"
> MDN, "Using HTTP cookies"
> MDN, "A typical HTTP session"
> MDN, "An overview of HTTP", HTTP is stateless, but not sessionless 항목 참고
> DEV Community, "Understanding Cookies and Sessions..." by mahdi
728x90'TIL-sparta' 카테고리의 다른 글
[TIL] 아이템 시뮬레이터 2 과제 시작 - 1일 차 (관계형 DB로 전환하기) (0) 2024.05.23 [TIL] 스파르타) Node.js 숙련주차 강의 수강 - 3일 차 (JWT) (0) 2024.05.22 [TIL] 스파르타) Node.js 숙련주차 강의 수강 (AWS RDS, Prisma) (0) 2024.05.20 [TIL] 프로그래머스) 76502 - 괄호 회전하기 (0) 2024.05.19 [TIL] 프로그래머스) 138476 - 귤 고르기 (Java) (0) 2024.05.18