HTTP 프로토콜은 요청을 받고 응답을 보내면 이전 정보는 기억하지 않는다. 그래서 브라우저에 쿠키를 담아 정보를 저장하는 방법을 사용했는데 이 방법으론 보안에 취약한 단점을 가지고있다.
사이트 간 스크립팅(XSS)
이란 브라우저에 악성 스크립트를 심어 신뢰하는 웹사이트를 사용하는 사용자들에게 공격하는 방법과 사이트 간 요청을 위조(CSRF)
는 사용자가 자신의 의지와는 상관없이 공격자의 의도 행위대로 공격하는 방법같은 보안 취약점이 존재한다.
그래서 상대적으로 안전한 세션(session)
을 알아보자.
세션
은 사용자가 웹사이트를 처음 시작부터 종료할 때 까지의 시간 개념을 말한다. 세션
또한 쿠키
를 기반으로 하고 있지만 서버에서 관리한다.
어쨌든 세션도 쿠키
를 사용해야하니 세션(session)/쿠키(cookie)
라고도 한다.
imageRef: hazelcast.com
사용자가 로그인을 한다.
서버는 세션ID
를 생성하고, 사용자에게 전송하여, 브라우저의 쿠키에 저장한다.
사용자가 웹페이지의 특정 요청(requset)을 하게되면 세션 ID가 담긴 쿠키를 헤더에 담아 전송한다.
전달받은 서버는 세션 ID를 기반으로 세션에 담긴 정보를 가져온다.
해당 정보로 서버 요청을 처리하여 클라이언트에게 응답한다.
각 클라이언트가 요청을 보내면 고유한 세션ID
를 부여한다.
사용자가 처음 접속부터 브라우저를 종료할 때 까지 인증상태를 유지한다.
접속 시간에 제한을 두어 일정시간 응답이 없으면 유지되지 않게 설정할 수 있다.
트래픽이 많을 경우 즉, 사용자가 많아질수록 서버의 메모리를 많이 차지한다.
서버에서 관리하기 때문에 쿠키보다 보안면에서 우수하다.
이렇게 봤을 때는 쿠키를 기반으로 해서 그런지 많이 유사한 모습을 볼 수 있다. 가장 쉽게 구분짓는 방법으론 쿠키는 클라이언트, 세션은 서버에 담는다는 것이다.
그래도 가장 큰 차이점이 존재하는데 다음과 같다.
세션은 서버의 자원을 사용한다.
단, 서버의 자원을 사용하기 때문에 트래픽이 많을 시 퍼포먼스가 떨어진다.
쿠키처럼 세션도 만료시간을 정할 수 있으며, 브라우저가 종료되면 만료시간 상관없이 삭제된다.
쿠키는 요청(request)에서 탈취당할 우려가 있지만, 세션은 쿠키에 세션ID
만 저장하고 서버에서 처리하기 때문에 보안상 더 우수하다.
쿠키의 경우.. 로그인을 안해도 장바구니에 물건을 담아둔 정보, 자동 로그인 및 팝업창 "하루 보이지 않기" 등 민감하지 않는 정보를 담아둔다.
세션의 경우 민감한 개인정보를 담아두며, 서버 퍼포먼스 상 이득을 챙기기 위하여 세션 관리 서버를 따로 만들어 관리한다고 알고있다.
본인은 node.js
,express ...
,passport ....
등등 라이브러리를 사용하고 있으며, Sequelize
라는 ORM(Object-Relational Mapping) 라이브러리를 사용하고 있다.
기본 express
와 session
라이브러리를 활용하여 구현하는 법과 passport
를 활용해 구현하는 법을 알아보자.
express-session
이 필요한 패키지이다. 그러나 해당 패키지만으론 저장소를 구현할 수가 없다.. 이점이 가장 아쉬운데 공식 라이브러리 npm
페이지에선 친절히 알려주고 있다.
두 방법으로 저장하는데, MYSQL session
과 redis 스토리지
를 활용한다. 취향껏 결정하자.
npm i redis connect-redis express-session --save
하지만 나는 MYSQL
을 활용할 것이므로, 아래의 명령어를 통해 패키지를 설치한다.
npm i express-mysql-session express-session --save
const mysql2 = require("mysql2/promise")
const session = require("express-session");
const MySQLStore = require("express-mysql-session")(session);
const options = {
host: 'localhost',
port: 3306,
user: 'your db username',
password: 'your db password',
database: 'your db name'
}
const connection = mysql2.createPool(options);
const sessionStore = new MySQLStore({}, connection);
app.use(session({
key: "session_cookie_name",
secret: "session_cookie_secret",
store: sessionStore,
resave: false,
saveUninitialized: false,
})
....
MySQL2
사용하므로 const mysql2 = require("mysql2/promise")
이렇게 불러와주었다.
로그인은 cookie
대신 session
으로 할경우는 다음과 같다.
router.post('/login', async (req,res) => {
const {email, password} = req.body
const users = await User.findOne({ where: { email } });
const bcryptPwd = await bcrypt.compareSync(password, users.password)
if(email === users.email && password === bcryptPwd){
req.session.user = {
uid = users.id
email = users.email;
}
res
.send("login success");
.redirect('/');
} else {
res
.send('Invalid email or password')
.redirect('/');
}
}
로그아웃은 세션을 날려버리면된다.
router.get('/logout',(req,res) => {
req.session.destroy();
res.redirect('/');
});
아니면 하나만 날리는 방법으로
router.get('/logout',(req,res) => {
delete req.session.user
req.session.save(() => {
res.redirect('/');
}
});
이렇게 해주면된다.