쿠키,세션,토큰(JWT)

박찬섭·2023년 7월 3일
0

네트워크

목록 보기
2/2

등장 계기
쿠키
세션
토큰(JWT)
간단요약

등장 계기

쿠키,세션,토큰, JWT 는 어떻게 등장을 했을까, 웹 서비스를 제공하면서 브라우저와 서버가 일을 하게 된다.
사용자가 브라우저를 통해 서버에게 요청을 하고 서버는 그 요청을 처리 해 주면서 우리는 그 서비스를 제공 받는다.
이때 문제가 발생하는데 사용자가 많아지면 많아질수록 관리해야 하는 유저도 많아진다.
일주일간 보지 않기 선택한 언어 화면 크기 등등 수 많은 유저들의 상태를 서버 혼자서 연산, 저장을 하기에는 서버에 무리가 크다.
HTTP통신의 특징 중 하나인 무상태성 즉 네트워크에서의 비용을 줄이기 위해서가 크다.
이때 클라이언트쪽에 상태나 값을 저장하여 서버에 요청을 할때마다 저장해 놨던 쿠키를 같이 보내서 서버가 보고 쿠키를 보고 처리 해 주는 방식이 등장한다.
클라이언트의 여러가지 정보들을 쿠키만의 방식으로 저장하면 보안,정확성의 문제가 생긴다.
중간에 쿠키를 탈취해서 수정 및 삭제같은 문제가 생기고 이러한 문제점들을 개선하기 위해 세션,토큰,JWT같은 것들이 등장한다.

쿠키

쿠키는 하나의 데이터를 주고 받는 방식 중의 하나다.
세션, 토큰, JWT모두 쿠키방식으로 정보를 주고 받는다.
JAVASCRIPT에서 쿠키를 보내거나 서버에서 확인하는 방법은 다음과 같다.

//서버(EXPRESS)
const express = require('express');
/*-------------기타 모듈생략-----------------*/
const cookieParser = require('cookie-parser');

//브라우저에서 보내는 쿠키를 res.cookies에 객체 형태로 파싱해줌
app.use(cookieParser());

//ex.. cookieId='test'라는 쿠키가 브라우저에서 왔다고 칠때
console.log(res.cookies.cookieId) //test

//브라우저에게 쿠키를 생성하라고 할때
res.cookie(Id,Value,cookieoptions);
//Id=쿠키의 아이디 ex)language
//Value=Id의 값 ex)Korean
//cookieoptions=쿠키의 옵션들

//cookieoptions 설정들
const cookieoptions = {
	domain:'localhost',	//쿠키를 전송할 도메인지정, 해당 도메인과 그 하위 도메인에 대해서만 쿠키 전송
  	path:"/",			//쿠키를 전송할 경로 '/'일 경우 모든 경로에서 쿠키 전송
  	secure:true,		//https에서만 쿠키 전송 허가
  	httpOnly:true,		//javaScript를 통해 쿠키에 접근 금지
  	sameSite:'strict'	//어떤 상황에서 서버로 전송될지를 정해줌 strict인 경우 사이트 간 쿠키 요청 불가능
}

//브라우저에게 쿠키를 삭제하라고 할때
res.clearCookie(Id)	//Id의 쿠키를 삭제명령

세션

사이트를 이용하면 대부분 맨 처음 로그인을 하면 그 사이트의 다른 서비스를 이용하더라도 재 로그인을 하라고 하지 않는다. 즉 사용자 편의를 위해 한번 로그인이 되면 그 뒤는 로그인을 유지시켜주는 것이다.
이 기능을 쿠키로만 구현한다면 사용자의 아이디 비밀번호를 쿠키로 저장하고 서버에 요청을 할 때마다 쿠키로 저장된 아이디와 비밀번호를 계속 서버에 전달 해 줘야 한다. 탈취의 위험성이 크다.
이때 세션이라는 개념이 등장한다.

작동방식은 다음과 같다.
1. 처음 로그인할때 ID,PW를 서버에 전달
2. 해당 ID와 PW가 일치하면 유니크한 세션ID를 생성 및 전달(브라우저에 쿠키형태로 저장), 유저의 정보도 저장
3. 서버에서는 세션DB에 발급해준 세션ID저장

이렇게 한다면 로그인을 유지하는데 계속 ID와 PW를 줄 필요없이 고유한 세션ID만을 가지고 서버에 세션DB에 그 세션ID가 존재하면 로그인을 유지시켜주고 필요한 정보를 꺼내서 줌

JAVASCRIPT에서 세션 사용법

//서버(EXPRESS)
const express = require('express');
/*-------------기타 모듈생략-----------------*/
const session = require('express-session');

app.use(
  session({
    secret: 'secret',	//세션ID에 추가적인 서명을 해줌
    resave: false,		//요청이 왔을때 세션에 수정 사항이 생기지 않더라도 다시 저정하지말지
    saveUninitialized: true,	//세션에 저장할 내용이 없더라도 처음부터 세션을 설정할지 
    cookie: {			//세션ID를 저장할 쿠키의 옵션
      domain: 'localhost',
      path: '/',
      maxAge: 3600,
      sameSite: 'none',
      httpOnly: false,
      secure: true,
    },
  })
);

//세션ID 생성 및 쿠키방식으로 브라우저에게 전달
res.session.test = '박찬섭';	//connect.sid=유니크한ID

//서버에서 세션ID확인
app.get('/', (req, res) => {
  const sessionID = req.sessionID;
  const sessionStore = req.sessionStore;

  // 세션 스토어를 사용하여 세션 데이터 조회
  sessionStore.get(sessionID, (err, sessionData) => {
    if (err) {
      // 에러 처리
      return res.status(500).send('Internal Server Error');
    }

    if (sessionData) {
      // 세션 데이터가 존재하면 인증된 세션임을 확인할 수 있습니다.
      console.log('인증된 세션 ID:', sessionID);
      console.log('세션 데이터:', sessionData);
    } else {
      // 세션 데이터가 없으면 인증되지 않은 세션임을 확인할 수 있습니다.
      console.log('인증되지 않은 세션 ID:', sessionID);
    }

    // 응답 처리
    res.send();
  });
});

토큰(JWT)

세션ID의 단점 및 개선을 위해 만들어진 것이 토큰이다.
세션ID의 단점은 세션DB에 현재 유요한 세션ID를 저장해야 한다는 단점이 존재한다.
사용자가 많아진다면 서버의 리소를 많이 먹게된다.

토큰은 JSON방식으로 header,payload,secret 부분으로 나뉘어서 서명처리한다.

이렇게 만들어진 토큰을 브라우저의 쿠키에 저장하고 서버는 암호화 할 때 추가로 붙인 secret의 값만 가지고 있으면 언제든지 토큰과 같이 요청이 오면 secret을 가지고 복호화를 통해 유효한 토큰인지 아닌지를 판별 할 수 있다.
서버의 과부화를 줄일 수 있다.
하지만 토큰이 탈취 되었을때 탈취한 해커가 해당 토큰으로 문제없이 접근 할 수 있다.
위의 문제를 해결하기 위해 토큰의 만료기간을 짧게 준다면 자주 로그인 해야 되는 편의성이 줄어든다.

require('dotenv').config();
const { sign, verify } = require('jsonwebtoken');

module.exports = {
  generateToken: (user, checkedKeepLogin) => {
    const payload = {
      id: user.id,
      email: user.email,
    };
    let result = {
      accessToken: sign(payload, process.env.ACCESS_SECRET, {expiresIn: '1h'}),
    };

    if (checkedKeepLogin) {
      result.refreshToken = sign(payload, process.env.REFRESH_SECRET, {expiresIn: '7d'});
    }
    return result;
  },
  verifyToken: (type, token) => {
    let secretKey, decoded;
    switch (type) {
      case 'access':
        secretKey = process.env.ACCESS_SECRET;
        break;
      case 'refresh':
        secretKey = process.env.REFRESH_SECRET;
        break;
      default:
        return null;
    }

    try {
      decoded = verify(token, secretKey);
    } catch (err) {
      console.log(`JWT Error: ${err.message}`);
      return null;
    }
    return decoded;
  },
};

환경변수 process.env의 값을 가지고 서명 및 복호화 과정을 거치는 코드이다.

간단요약

쿠키 : 브라우저와 서버가 데이터를 주고받는 하나의 방법
세션 : 비교적 중요한 데이터를 서버에서 관리하기 위해 유효성을 인증시켜줄 세션ID를 쿠키로 발급
토큰 : 서버의 리소르를 줄이기 위해 암호화된 토큰을 쿠키로 발급 서버는 암호화 할 때의 비밀키로 복호화해서 유효성 검사

profile
백엔드 개발자를 희망하는

0개의 댓글