[WebDevCurriculum] 로그인 정보 jwt 발급 및 cookie 저장

Hyo Kyun Lee·2021년 11월 19일
0

WebDevCurriculum

목록 보기
28/44

1. 사용자 로그인 정보를 저장, 유지

로그인 정보를 안전하게 유지하는 방법

사용자 로그인 정보를 안전하게 저장하고, 이를 유지(지속)하기 위한 여러 방안들이 존재한다.

암호화

  • 사용자가 입력한 로그인 정보(ID, PW)를 암호화(혹은 hash)한다.
  • 사용자 로그인 정보를 암호화한 후, 이를 jwt로 발급한다.

정보저장

  • 로그인 정보를 localstorage에 저장하여 유지한다.
  • jwt로 발급한 로그인 정보를 cookie에 보관한다.
  • jwt로 발급한 로그인 정보를 session(server)에 보관한다.

2. 사용자 정보를 어디에 저장하는 것이 좋을까

답은 정해져 있지 않다.

그나마 안전한 방법은 사용자의 로그인 정보를 로그인을 하였을 때만 유효하도록 하고, 브라우저를 닫으면 정보를 폐기하는 것이다.

하지만 로그인 정보를 유지하기위해 webstorage, jwt, cookie 등의 기능을 활용하게 되었는데, 이 기능들은 쉽게 공격에 노출될 수 있으므로 신중하게 활용해야 한다.

webstorage

client 측에서 자신의 data를 보관할 수 있는 곳은 localstorage, sessionstorage로 두가지가 존재한다.

localstorage는 영구적으로 data를 저장할 수 있고, 그만큼 쉽게 공격에 노출되어 있을 수 밖에 없다.

sessionstorage는 session id로 구성된 session 정보를 저장하기 위해 존재하는 저장소이며, localstorage와 달리 만료기한이 지나면 정보가 폐기된다.

jwt

jwt는 secrect key(혹은 RSA)를 통해 암호화를 하여 정보를 저장하는 token이다.

header - payload - signature로 구성되어 있고, payload에 사용자 정보가 저장이 된다.

signature 정보를 확인해서 정보가 외부 공격에 노출되었는지 확인할 수 있고, 별도의 옵션(httponly)을 부여하면 데이터 위변조를 확인할 수 있다.

cookie

정해진 만료기한만큼 client 측에 저장되어있는 정보이다.

cookie에 저장되어있는 정보는 장바구니, 즐겨찾는 사이트 등 비교적 보안적으로 중요하지 않은 것들이다.

사용자 정보와 같은 보안적으로 중요한 것들은 웬만해서는 cookie에 넣어선 안된다.

cookie의 보안성이나 저장 등의 단점을 보완하기 위해 webstorage가 고안되었는데, 이 storage마저도 공격에 노출되어 안전한 저장소로 보기엔 무리가 있다.

(※ 차선책은 session(server)에 저장하여 관리하는 것이고, 이는 다른 포스트에서 설명)

3. localstorage를 활용한 정보저장 및 유지

이전에 정리한 부분이 있으므로 이곳에서 참조할 것

4. jwt / cookie를 활용한 정보저장 및 유지

cookie를 활용한다는 것에 의미를 둔다.

최종적으로 jwt 정보는 session에 저장하는 것으로 구현할 예정이지만, cookie에 저장하는 방법도 알고있어야 전반적인 인증 logic을 이해할 수 있기 때문에 같이 학습한다.

4-1. jwt 생성(발급)

필요한 module

const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');

router.get('/', (req, res) => {
    res.render('jwt.html');
});
  • express, router : express API를 활용하고, expressRouter를 통해 url path를 분리하는 미들웨어를 구성할 수 있다.
  • jsonwebtoken : jwt를 생성하기 위해 필요한 module이다.

jwt token 생성

jwt.sign()을 통해 jwt token을 생성할 수 있으며, 아래 예시는 사용자가 입력한 ID/PW를 jwt token에 저장하는 기본적인 예시이다.

router.post('/add', (req, res) => {
    //console.log(req.body);
    let {ID, PW} = req.body;

    const token = jwt.sign(
        {
            userID: ID,
            userPW: PW
        }, 
        'secret', 
        {
            expiresIn: '1h'
        }
    );

    console.log('token is, ' + token);
    
    res.redirect('/jwt');
});
  • 입력받은 ID, PW 값은 req.body에서 비구조화한 변수(ID, PW)에 저장된다.
  • jwt.sign을 통해 token을 생성할 수 있고, ID와 PW변수를 token의 payload에 저장한다.
  • 'secret'은 해당 정보를 암호화하기 위한 secret-key이고, 이를 복호화할 수 있는 key는 동일한 secret-key 이다.
  • option에 expiresIn(만료기한) 등을 지정해준다.

log 확인

token이 잘 생성되었는지 확인한다.

4-2. cookie로 저장

필요한 module

핵심 module은 cookie parser이다.

const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');

res.cookie('cookiename', token)

res.cookie를 활용하면 생성한 jwt token을 cookie에 저장할 수 있다.

router.use('/', cookieParser());

router.get('/', (req, res) => {
    res.render('jwt.html');
});

router.post('/add', (req, res) => {
    //console.log(req.body);
    let {ID, PW} = req.body;

    const token = jwt.sign(
        {
            userID: ID,
            userPW: PW
        }, 
        'secret', 
        {
            expiresIn: '1h'
        }
    );

    console.log('token is, ' + token);
    
    //JWT save to cookie (*cookie-parser)
    res.cookie('loginCookie', token, {
    });
    res.redirect('/jwt');
});
  • res.cookie('loginCookie', token, {option}'), 이를 통해 사용자 정보를 저장한 token은 loginCookie라는 이름으로 cookie에 저장된다.
  • cookie에 저장된 정보들은 사용자 요청(req)에 저장되므로, 이를 활용하여 정보인증 및 지속에 사용할 수 있다.
  • router.use('/', cookieParser()); 으로 미들웨어 설정을 해주어야, 해당res cookie에 정보가 저장되고 req에서 가져올 수 있다.

4-3. cookie에서 정보 가져오기

cookie-parser를 이용하면 req에서 cookie 정보를 가져올 수 있다.

router.post('/check', (req, res) => {
    
    //get login token from cookies
    let reqCookie = req.cookies;
    let token = reqCookie.loginCookie;

    let {ID, PW} = req.body;

    //decode
    const decoded_data = jwt.verify(
        token, 
        'secret',
        //callback 내부의 decoded data는 외부에서 재활용 할 수 없다
        //callback은 확인용
        /*(err, decoded) => {
            console.log(decoded);
        }*/
    );
    
    if(ID, PW){
        if(ID == decoded_data.userID && PW == decoded_data.userPW){
            res.send('CORRECT ID / PW');
        }else{
            res.send('INCORRECT ID OR PW');
        }
    }

    //data 활용용도, 위 callback에서 사용한 이력이 없어야 한다.
    //아래에서의 decoded data는 보여지지 않고, 직접 접근해야 한다.
    console.log('dedoced data is, ' + decoded_data.userID);
    console.log('dedoced data is, ' + decoded_data.userPW);
    
})
  • req.body에서 ID/PW 정보를 추출하고, req.cookies에서 cookie 정보를 추출한다.
  • req.cookies에 저장된 정보는 객체로 저장되어있는 jwt이고, 여기에 접근하기위해 reqCookie.loginCookie(사용자가 지정한 이름)을 적어준다.
  • jwt.verify 를 통해 저장한 token 정보를 decode할 수 있고, 이때 반드시 secret-key를 적어주어야 한다.
  • jwt.verify로 인증한 정보를 callback 함수에서 활용(console 등)할 경우엔 그 이후에 decoded_data를 읽어올 수 없으니 참조하도록 한다.

loginCookie라는 이름으로 jwt 정보가 저장되어있고, 이를 활용하면 로그인 정보를 유지하고 인증할 수 있다.

다만 cookie에 httponly 옵션을 추가할 수는 있으나, 민감한 정보가 들어있으면 안되므로 웬만해서는 사용하지 않도록 한다.

5. 참조링크

cookie 정보는 어디에 저장해야할까?
Cookie 저장 - https://medium.com/sjk5766/jwt-json-web-token-%EC%86%8C%EA%B0%9C-49e211c65b45
localStorage 저장 - https://velog.io/@0307kwon/JWT%EB%8A%94-%EC%96%B4%EB%94%94%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C-localStorage-vs-cookie
session(server) 저장 -

jwt 생성/발급하기
https://www.youtube.com/watch?v=0D5EEKH97NA
https://medium.com/sjk5766/jwt-json-web-token-%EC%86%8C%EA%B0%9C-49e211c65b45

cookie parser를 활용하여 jwt 정보를 cookie에 저장하기
https://berkbach.com/node-js-%EC%99%80-cookie-session%EC%9C%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90%EC%9D%98-%EC%A0%95%EB%B3%B4-%EC%A0%80%EC%9E%A5-part-1-b66d8b35a6e6

jwt 공식 문서
https://jwt.io/

0개의 댓글