포시코딩

[코드이슈] authMiddleware의 보안 이슈 - 작성중 본문

Node.js

[코드이슈] authMiddleware의 보안 이슈 - 작성중

포시 2023. 1. 15. 23:04
728x90

https://github.com/9hezo/save_my_keyboard/blob/dev/app/src/middlewares/authMiddleware.js

 

GitHub - 9hezo/save_my_keyboard

Contribute to 9hezo/save_my_keyboard development by creating an account on GitHub.

github.com

 

개요

'use strict';

const TokensService = require('../services/tokens.service');
const UsersService = require('../services/users.service');
const TokenManager = require('../config/TokenManager');

module.exports = async (req, res, next) => {
  const tokensService = new TokensService();
  const usersService = new UsersService();

  console.log('path: ', req.path);

  const { accessToken, refreshToken } = req.cookies;

  if (!refreshToken || !accessToken) {
    // console.log('refreshToken 또는 accessToken가 쿠키 목록에 존재하지 않습니다.');
    return next();
  }

  const isAccessTokenValidate = TokenManager.validateAccessToken(accessToken);
  const isRefreshTokenValidate = TokenManager.validateRefreshToken(refreshToken);

  if (!isRefreshTokenValidate) {
    console.log('Refresh Token이 만료되었습니다.');
    res.clearCookie('accessToken');
    res.clearCookie('refreshToken');
    return next();
  }

  let userId = -1;
  if (!isAccessTokenValidate) {
    const tokenInfo = await tokensService.findOneToken(refreshToken);

    if (!tokenInfo) {
      console.log('Refresh Token의 정보가 서버에 존재하지 않습니다. 모든 토큰 값을 제거합니다.');
      res.clearCookie('accessToken');
      res.clearCookie('refreshToken');
      return next();
    }

    const newAccessToken = await TokenManager.createAccessToken(tokenInfo.userId);
    res.cookie('accessToken', newAccessToken);

    userId = TokenManager.getAccessTokenPayload(newAccessToken).userId;
  } else {
    userId = TokenManager.getAccessTokenPayload(accessToken).userId;
  }
  const userInfo = await usersService.findUserById(userId);
  res.locals.userInfo = { id: userInfo.id, name: userInfo.name, point: userInfo.point, admin: userInfo.admin };
  next();
};

 

accessToken, refreshToken을 활용한 로그인 시스템에 배운 당시에 진행했던 프로젝트에서

만든 위 authMiddleware를 지금까지 진행했던 프로젝트에서 모두 사용해왔는데

 

유지보수 하는 과정에서 치명적인 보안 이슈가 있는게 확인되어
아예 다 갈아엎고 새롭게 만들어야 되는 상황이 발생했다.

확인된 문제는 다음과 같다.

accessToken, refreshToken 둘 다 탈취되는 상황 뿐만 아니라
refreshToken만 탈취 당했더라도 아래 사진과 같이 accessToken은 대충 아무거나 적고
refreshToken만 제대로 적으면 유효기간이 긴 refreshToken을 통해 
새롭게 accessToken을 발급받아 로그인 처리가 이루어짐 

 

상황 가정

1. 탈취한 refreshToken 쿠키로 등록

 

2. 그냥 refreshToken만 있다면 accessToken이 없어 로그인 되지 않지만, accessToken을 아무 값이나 등록해주면

3. 로그인 되는 모습을 볼 수 있다.

 

진행 상황

안그래도 새로 프로젝트 들어가기 전에 애매했던 로그인 인증 토큰 처리 방법에 대해

한번 짚고 넘어가야겠다 생각하고 있었는데

 

이렇게 문제가 심각한 상황일줄은 몰랐다.

 

이걸 좋다고 여러 프로젝트에서 써오면서 다른 사람들한테도 참고하라고 공유했었다니..

 

글을 쓰는 지금도 refreshToken만 복붙했을 뿐인데 로그인 될 때의 상황을 생각하면 소름이 끼친다. 으으

 

하루빨리 어떻게 해결하면 좋을지 방법을 강구해 다시 포스팅을 이어 할 계획이다.

 

passport를 통한 session based 인증

https://github.com/cchoseonghun/nodejs_login_prac/tree/main/session_using_passport

 

GitHub - cchoseonghun/nodejs_login_prac: session, jwt 등을 통한 여러 사용자 인증 방법 공부

session, jwt 등을 통한 여러 사용자 인증 방법 공부. Contribute to cchoseonghun/nodejs_login_prac development by creating an account on GitHub.

github.com

 

이번에는 express에서 passport를 이용해 session에 사용자 정보를 전달하고 

자동으로 쿠키가 발행되는 방법을 사용해봤다.

 

모든 기능을 구현한 후 테스트할 때는 이상이 없어 보였고

생성된 쿠키도 HttpOnly로 등록되어 어느정도 보안상의 이슈는 막아줄 수 있는 상황이 되었다.

 

하지만 마찬가지로 탈취되었다는 가정하에 복붙한 쿠키 값을 직접 적어넣으니

어김없이 로그인된 상태로 인식하여 사용자 정보를 받아오는 것을 확인할 수 있었다.

 

728x90