몰입공간

[JWT] JWT의 보안 전략 (How to use secured JWT) 본문

Programming/Web

[JWT] JWT의 보안 전략 (How to use secured JWT)

sahayana 2022. 6. 24. 16:07


#1. JWT

 

JWT(Json Web Token)는 토큰 인증 방식 중 아마 가장 널리 쓰이는 라이브러리입니다.
관련 커뮤니티도 활발하고, 자체적으로 사용법에 대한 E-BOOK도 제공하는 것으로 알고 있습니다.

JWT가 인기있는 인증 모듈로 각광을 받게된건 다음과 같은 이유라고 생각합니다.

 

  • 확장이 용이하다. (Scalability)
  • 사용하기 편하다. (Simplicity)
  • 서버에 저장하지 않는다. (Stateless)

어쩌다보니 3S를 맞추려고 어거지로 작성한 느낌이 있는데 결국 다 연관있는 이야기입니다.

이런 인기와 맞물려 무분별하게 JWT를 쓰는 경향과 이에 따른 보안이슈에 대해 목소리를 내는 개발자들이 많은 걸
보았습니다.

 

실제로 "한물간" 세션 인증방식으로 회귀해야한다고 주장하는 경우가 대부분인데 어떠한 이유에서 이러한 목소리를
내는지, 그리고 JWT를 안전하게 쓰기 위한 방법은 무엇이 있는지 알아보겠습니다.

 

JWT에 관한 기본적인 정보는 공식문서 혹은 여기를 참조해도 좋습니다.

 

 

[Django] JWT 토큰 기반 인증 (authentication by JSON Web Token)

#1. 세션과 쿠키 그리고 토큰 토큰 인증 방식에 대해 서술한 대부분의 포스팅을 보면 세션과 쿠키 (+토큰)의 개념에 대한 설명을 거의 필수적으로 수반합니다. 매우 중요한 개념이긴 하지만 여기

sahayana.tistory.com

 


#2. 문제: 토큰 하이재킹

 

토큰 인증 방식은 대부분 로그인 절차를 끝내면 response로 토큰을 명시하고 이를 프론트로 넘겨줍니다.

혹은 인증 과정에서 클라이언트의 토큰 저장 위치가 Local storage인 경우 보호받지 못하는 경우가 대부분이라
토큰 하이재킹, 즉 탈취당할 위험이 있습니다.

 

이 경우 다음과 같은 방법으로 보완할 수 있습니다.

  • 토큰은 브라우저 쿠키에 저장한다.
  • 쿠키에 저장한 토큰 보호를 위한 HTTP 헤더 플래그 옵션을 설정한다.
    • Secure: HTTPS 프로토콜 위에서 암호화된 요청만 전달하는 옵션
    • HttpOnly: 자바스크립트는 Document.cookie API에 접근할 수 없고 오직 쿠키만 서버로 전달 (XSS 공격 방어)
    • SameSite: strict 옵션을 줄 경우 쿠키가 동일한 사이트 내의 요청으로만 전송 (CSRF 공격 방어)
  • 토큰 암호화 및 복호화 과정에 필요한 유저 정보를 재차 암호화 한다.(SHA256 해시, UUID 등)

#3. 문제: 토큰이 만료하기 전까지 서버는 토큰을 강제할 수 없다.

 

 

토큰의 만료 기간이 짧으면 짧을수록 로그인을 자주해야하고 이는 곧 서비스에 대한 사용자의 불편함을 야기시킵니다.
그렇다고해서 토큰의 만료시간을 무조건 길게 가져가는 것 역시 보안관점에서 좋은 방법은 아닙니다.

토큰은 만료하기 전까지 항상 유효하며 토큰에 문제가 생겨도 시스템(서버)은 토큰을 강제할 방법이 없습니다. 
서버는 토큰을 복호화하는 검증 절차만 가지며 토큰 자체는 클라이언트가 가지고 있기 때문입니다.

특히 토큰의 긴 만료시간 + local storage 저장은 공격에 매우 취약한 조합입니다.

 

이 경우 다음과 같은 방법으로 보완할 수 있습니다.

 

  • 긴급조치로 암호화 수단인 서버의 비밀키를 변경합니다. 이 경우 서비스에 접속하는 모든 유저가 다시 로그인을 합니다.

  • Access token의 만료를 짧게 설정하되 Refresh Token을 같이 발급하여 access token 만료 혹은 만료 전 새로 갱신합니다.
    • Refresh token을 추가로 발급하는 것은 이제 널리 알려진 보안 방법 중 하나입니다.
    • 특히 Refresh token은 특성상 매우 안전한 곳에 보관하기 때문에 보통 데이터베이스에 저장하는 경우가 많습니다.
  • 토큰 블랙리스트를 구현합니다.
    • 이 방법은 유저가 의도적으로 logout 하는 경우를 의미합니다.
    • 유저가 logout 하게 되면 기존의 토큰을 blacklist에 저장합니다.
    • 유저가 다시 로그인을 하면 유저가 가진 토큰을 blacklist와 비교 후 존재한다면, 기존의 토큰을 말소시키고 새로운 토큰을 발급하는 방법입니다.

 


#4. 정리

 

진행중인 서비스가 굳이 확장을 고려할 필요가 없다면 session 기반의 인증 방식을 채택하는 것도 나쁘지 않습니다.

또한 위에서 정의한 헤더 플래그를 정의하는 것만으로 완벽하게 XSS나 CSRF 공격을 막을 수 있는 것도 아니라고는

하네요.

 

안전한 인증을 구현하는 방법은 JWT나 서버사이드 세션 이외에도 많은 방법이 있습니다.

자격증명 프로덕트인 AWS Cognito 혹은 Firebase 등의 써드파티 라이브러리를 사용하거나 redis등의 인메모리 DB에 토큰 혹은 세션id를 숨기거나 하는 방법도 많은 것으로 알고 있습니다.

아무래도 사용자 인증 절차에서 발생하는 보안문제가 많다보니 이러한 보안 이슈를 해결할 방법을 탐구하고 공부하는 것도 꽤 재미있다고 생각하네요.

 


#5. 참고

 

Comments