JWT 인증 시스템 보안 강화
저번 웹 프로젝트에서 Access Token(JWT)를 통한 인증 방식을 사용했습니다.
이 인증 방식의 문제점은 만일 제 3자에게 탈취당할 경우 보안에 취약한 문제점이 있었습니다.
이 문제를 해결하기 위해 토큰의 유효기간을 설정하는 방법이 있습니다.
토큰의 유효기간을 짧게 하는 경우 그만큼 사용자는 로그인을 자주 해서 새롭게 토큰을 발급받아야 하므로 불편합니다.
토큰의 유효기간을 길게 하는 경우 토큰을 탈취당했을 때 보안에 더 취약해지게 됩니다.
그러면 유효기간을 짧게 하면서 좋은 방법이 있지 않을까? 라는 질문이 듭니다.
답은 바로 Refresh Token 입니다.
기존의 Acess Token만을 이용한 서버 인증 방식에 대해 알아보겠습니다.
- 사용자가 로그인을 합니다.
- 서버에서 사용자 확인 후, Access Token(JWT)에 권한 인증을 위한 정보를 Payload에 넣고 생성합니다.
- 생성한 토큰을 클라이언트에게 반환하고, 클라이언트는 이 토큰을 저장합니다.
- 클라이언트는 권한 인증이 필요한 요청을 할 때마다 이 토큰을 헤더에 실어 보냅니다.
- 서버는 헤더의 토큰을 검증하고, Payload의 값을 디코딩하여 사용자의 권한을 확인하고 데이터를 반환합니다.
- 만약, 토큰이 valid하지 않거나 만료되었다면 새로 로그인을 하여 토큰을 발급 받아야 합니다.
Access Token만을 이용한 인증 방식의 문제는 제 3자에게 토큰을 탈취 당하게 되면,
토큰의 유효 기간이 만료되기 전까지는 막을 방법이 없다는 점입니다.
그렇기에 대부분 토큰의 유효 기간을 짧게 설정합니다.
하지만, 짧게 설정하면 로그인을 자주 해야 하는 단점이 있어 사용자가 불편을 겪게 됩니다. 이를 어떻게 해결할까요?
Refresh Token으로 해결하게 됩니다.
Refresh Token이란 Access Token과 같은 JWT입니다. 하지만 차이점이 있다면, Refresh Token은 아주 긴 유효기간을 가지며 Access Token이 만료되었을 때 새로 발급을 해주기 위한 토큰이라는 점입니다.
그래서 보통 Acess Token의 유효 기간을 1시간, Refresh Token의 유효기간을 2주정도로 정합니다.
그 후 Access Token이 만료되었을 때, Refresh Token이 만료되지 않았다면 Access Token을 재발급하는 형태로 인증을 하게 됩니다.
- 사용자가 ID , PW를 통해 로그인합니다.
- 서버에서는 회원 DB에서 값을 비교합니다(보통 PW는 일반적으로 암호화해서 들어갑니다)
- 로그인이 완료되면 Access Token, Refresh Token을 발급합니다. 이때 일반적으로 회원DB에 Refresh Token을 저장해둡니다.
- 클라이언트는 두 토큰을 저장합니다.
- 사용자는 Refresh Token은 안전한 저장소에 저장 후, Access Token을 헤더에 실어 요청을 보냅니다.
- Access Token을 검증하여 이에 맞는 데이터를 보냅니다.
- 데이터를 클라이언트에 보냅니다.
- 시간이 지나 Access Token이 만료됐다고 보겠습니다.
- 사용자는 이전과 동일하게 Access Token을 헤더에 실어 요청을 보냅니다.
- 서버는 Access Token이 만료됨을 확인합니다.
- 클라이언트에 Access Token권한 없음(만료)을 신호로 보냅니다.
- 사용자는 Refresh Token과 Access Token을 함께 서버로 보냅니다.
- 서버는 받은 Access Token이 조작되지 않았는지 확인한후, Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교합니다. Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 발급해줍니다.
- 서버는 새로운 Access Token을 헤더에 실어 다시 API 요청을 진행합니다.
정리를 하자면
Refresh Token & Access Token의 장단점
장점 : 기존의 Access Token만 있을 때보다 보안성 우수
단점 :
- 구현이 복잡합니다. 검증 프로세스가 길기 때문에 자연스레 구현하기 힘들어졌습니다(프론트엔드, 서버 모두)
- Access Token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 HTTP 요청 횟수가 많습니다. 이는 서버의 자원 낭비로 귀결됩니다.
구현 시나리오는 다음과 같습니다.
시나리오 순서
- 로그인을 하면 Access Token과 Refresh Token을 모두 발급한다.
이때, Refresh Token만 서버측의 DB에 저장하며 Refresh Token과 Access Token을 쿠키에 저장한다. - 사용자가 인증이 필요한 API에 접근하고자 하면, 가장 먼저 토큰을 검사하는 미들웨어를 검사한다.
이때, 토큰을 검사함과 동시에 각 경우에 대해서 토큰의 유효기간을 확인하여 재발급 여부를 결정한다. - 로그아웃을 하면 Access Token과 Refresh Token을 모두 만료시킨다.
Case
- access token과 refresh token 모두가 만료된 경우 -> 에러 발생
- access token은 만료됐지만, refresh token은 유효한 경우 -> access token 재발급
- access token은 유효하지만, refresh token은 만료된 경우 -> refresh token 재발급
- accesss token과 refresh token 모두가 유효한 경우 -> 다음 미들웨어로
https://cotak.tistory.com/102
https://tansfil.tistory.com/59
https://velog.io/@kshired/Express%EC%97%90%EC%84%9C-JWT%EB%A1%9C-%EC%9D%B8%EC%A6%9D%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-Access-Token%EA%B3%BC-Refresh-Token