세션은 쿠키와 다르게 중요한 데이터를 서버에 저장한다.
중요한 데이터를 클라이언트에 전달할 때 직접 전달하지 않고 암호화 된 상태로 전달한다.
마치 신분증과 같은 역할을 하는 것.
세션은 서버에서 추가적인 검증을 하므로 좀 더 보안에 용이하다.
Node.js에서는 세션을 관리해주는 express-session
이라는 모듈이 있다.
const express = require('express');
const session = require('express-session');
const app = express();
app.use(
session({
secret: '@codestates', // 비밀키 이용
resave: false,
saveUninitialized: true,
cookie: {
domain: 'localhost',
path: '/',
maxAge: 24 * 6 * 60 * 10000,
sameSite: 'none',
httpOnly: false,
secure: true,
},
})
);
https://velog.io/@liso_o/TIL-17-Cookie 상세하게 써논 로직
지난번 쿠키와 같은 로직을 사용한다. 다만, server 단에서 세션으로 받아오는 것의 차이가 있다.
//server/index.js
app.use(
session({
secret: '@codestates',
resave: false,
saveUninitialized: true,
cookie: {
domain: 'localhost',
path: '/',
httpOnly: true,
sameSite: 'strict',
},
})
);
서버에서 세션을 사용한다고 선언 후,
//server/login.js
const { USER_DATA } = require("../../db/data");
module.exports = (req, res) => {
const { userId, password } = req.body.loginInfo;
const { checkedKeepLogin } = req.body;
const userInfo = {
...USER_DATA.filter(
(user) => user.userId === userId && user.password === password
)[0],
};
if (!userInfo.id) {
res.status(401).send("Not Authorized");
} else if (checkedKeepLogin) {
req.session.sessionId = userInfo.id;
console.log(req.session);
}
};
로그인 처리에서 세션의 구조를 확인해보면 다음과 같이 나온다.
usefInfo에서 필터링된 id는 sessionId에 들어가고 그 외의 추가적인 옵션이 담겨져있는 쿠키는
req.session.cookie
안에 있다.
따라서 req.session.cookie
안에 정보를 넣어주면 된다.
express-session의 핵심은
req
객체를 사용하는 것!!res
아님 헷갈리지 않게 주의
//server/login.js
const { USER_DATA } = require('../../db/data');
module.exports = (req, res) => {
const { userId, password } = req.body.loginInfo;
const { checkedKeepLogin } = req.body;
const userInfo = {
...USER_DATA.filter((user) => user.userId === userId && user.password === password)[0],
};
if (!userInfo.id) {
res.status(401).send('Not Authorized');
} else if (checkedKeepLogin) {
req.session.userId = userInfo.id;
req.session.cookie.maxAge = 1000 * 60 * 30;
res.redirect('/userinfo');
} else {
req.session.userId = userInfo.id;
res.redirect('/userinfo');
}
};
이후 redirect로 보낸 데이터를 userinfo
에서 관리한다.
//server/userinfo.js
const { USER_DATA } = require('../../db/data');
module.exports = (req, res) => {
const userInfo = USER_DATA.filter((it)=>{
return it.id===req.session.userId
})[0];
if(!userInfo){
res.status(401).send("Not Authorized");
}
else{
delete userInfo.password;
res.send(userInfo);
// 혹은 이렇게 작성도 가능
// res.json({...userInfo,password:undefined});
}
};
세션에 담긴 userId와 알맞은 유저의 정보를 보내고,
세션 내의 쿠키의 옵션을 통해 추가적인 옵션을 부여해준다.
비밀번호는 민감한 정보니 꼭 삭제해서 보내자.
//server/logout.js
req.session.destroy();
res.status(205).send("Logged Out Successfully");
이후 로그아웃 시 세션을 없애준다.
토큰 기반 인증은 기존의 세션 기반 인증의 단점을 보완하기 위해 만들어졌다.
세션 기반 인증은 서버에서 유저의 상태를 관리해야한다.
만약 서버가 여러 개 있으면, 하나의 큰 store를 통해 서버끼리 세션 정보를 교환해야 한다.
이는, 서버에게 부담이 갈 수가 있다.
이를 해결하기 위한 토큰 기반 인증방식은, 유저의 인증상태를 서버가 아닌 클라이언트에 저장하는 방식이다.
토큰 전달 방식이다.
세션 기반 인증이랑 비슷해 보이지만, 서버 메모리에 저장하는 거와는 달리 토큰과 서버의 비밀 키를 생성해서 클라이언트에 전달해준다.
전달 해줄때 쿠키가 아닌 Authorization 헤더를 통해 전달 해주는데 이는 쿠키에 크기 제한이 있기 때문이다.
클라이언트는 요청과 함께 토큰을 전달하는데 이때 서버는 해당 토큰이 유효한 토큰인지를 검증한다.
이러한 토큰 기반 인증 방식을 대표적으로 사용하는 기술이 JSON Web Token (JWT)
방식이다.
JWT 방식은 JSON 객체에 정보를 담고 이를 토큰으로 암호화하여 전송하는 기술이다.
JWT는 다음과 같이 .
과 문자열로 구성된 부분이 존재하여 .
으로 세 부분으로 나눌 수 있다.
Header에는 HTTP의 헤더처럼 해당 토큰 자체를 설명하는 데이터가 담겨 있다.
{
"alg": "HS256",
"typ": "JWT"
}
이 JSON 객체를 base64방식으로 인코딩하면 JWT의 헤더 부분이 완성된다.
base64 방식은 원한다면 얼마든지 디코딩할 수 있는 인코딩 방식이다. 따라서 비밀번호와 같이 노출되어서는 안 되는 민감한 정보를 담지 않도록 해야 한다.
HTTP의 Payload와 마찬가지로 전달하려는 내용물이 담겨있다.
어떤 정보에 접근 가능한지, 토큰의 발급 만료 시간, 유저의 이름 같은 개인정보 등이 담겨있다.
{
"sub": "someInformation",
"name": "phillip",
"iat": 151623391
}
마찬가지로, 위 JSON을 base64로 인코딩하면 Payload가 완성된다.
Signature는 토큰의 무결성을 확인할 수 있는 부분이다.
Header와 Payload를 salt를 추가시켜 Header에서 지정한 알고리즘을 이용해 해싱한다.
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
무상태성 : 제 3자가 토큰을 탈취하는 경우 서버가 해당 토큰을 강제로 만료시킬 수 없다.
유효 기간 : 토큰 탈취를 대비해 토큰 유효기간을 짧게 설정하면 사용자가 불쾌한 경험을 가질 수 있다. (ex)재로그인 비중 증가)
토큰의 크기 : 토큰에 많은 데이터를 담으면 암호화하는 과정도 길어지며 클라이언트~서버 간의 네트워크 비용도 증가한다.
Access Token
액세스 토큰은 말 그대로 서버에 접근하기 위한 토큰으로 앞서 다룬 토큰과 비슷한 역할을 합니다. 따라서 보안을 위해 보통 24시간 정도의 짧은 유효기간이 설정되어 있습니다.
Refresh Token
리프레시 토큰은 서버 접근을 위한 토큰이 아닌 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급받기 위해 사용되는 토큰입니다. 따라서 리프레시 토큰은 액세스 토큰보다 긴 유효기간을 설정합니다.