본문 바로가기
Back-End/Spring Security

Spring Security의 JWT(JSON Web Token) 인증 | Refresh Token이 탈취될 경우

by 달의 조각 2022. 9. 26.

자격 증명 방식

 

  HTTP 프로토콜은 요청을 전송하고 응답을 받으면 연결을 끊는 비연결성 특성을 갖는다. 또한, 상태를 저장하지 않는 비상태성 특성을 가지므로 로그인이 성공적으로 수행되어도 서버 측에서는 그 이후에 수신된 요청이 인증된 사용자의 것인지 알 수 없다. 사용자 인증이 성공적으로 이루어졌을 때 사용자의 요청을 유지하기 위한 수단에는 무엇이 있을까?

 

🧩 세션 기반

  인증된 사용자 정보를 서버 측에서 세션 형태로 세션 저장소에 저장한다. 사용자 요청이 올 때마다 서버는 세션 저장소의 세션 정보와 사용자가 보낸 정보(쿠키의 세션 ID)가 일치하는지 확인한다. SSR 방식에 적합한 방식이다.

  • 사용자는 세션 ID만 가지므로 적은 네트워크 트래픽을 사용한다.
  • 세션 정보는 서버 측에서 관리하므로 보안성이 좋다.
  • 세션 데이터가 많아지면 서버의 부담이 가중된다.
  • 서버의 확장성 면에서 세션 불일치 문제가 발생할 가능성이 높다.
    • 모든 서버가 사용자의 세션 정보를 공유하고 있어야 한다.
    • 보완: Sticky SessionSession Clutering, Session 저장소 외부 분리

 

🪙 토큰 기반

  토큰이란 마패와 일회용 교통카드, 콘서트장에서 티켓을 보여 준 후 받는 종이 팔찌 등에 비유할 수 있다. 요청을 보낼 때 헤더에 생성된 토큰를 포함한다. 토큰으로 해당 서비스를 이용할 수 있는 사람이라는 것이 증명된다. 이렇게 증명된 사람은 접근 권한이 부여된 범위 내에서 서비스를 이용할 수 있다. CSR 방식에 적합한 방식이다.

  • 사용자 요청을 유지할 필요가 없으므로 서버 확장성이 좋고, 세션 불일치 문제가 발생하지 않는다.
  • 토큰 내 인증된 사용자 정보를 포함하므로 세션보다 많은 네트워크 트래픽을 사용한다.
  • 서버에서 토큰을 관리하지 않으므로 보안 측면에서는 좀 더 불리하다.
  • 토큰에 포함되는 사용자 정보는 특성상 암호화가 되지 않는다. 민감한 정보는 포함하지 않아야 한다.
  • 토큰이 만료되기 전까지 토큰을 무효화 시킬 수 없다.
    → 보완: key / value 쌍으로 저장되는 Redis 같은 인메모리 DB에 무효화 하고자 하는 토큰의 만료 시간을 짧게 준다.
 

 


 

JWT

JSON Web Token

 

  JWT는 데이터를 안전하고 간결하게 전송하기 위해 고안된 웹 표준 인증 방식이다. JSON 포맷의 토큰 정보를 인코딩 한 후, 인코딩 된 토큰 정보를 Secret Key로 서명(Sign)한 메시지를 Web Token으로써 인증 과정에 사용한다.

 

🪙 종류

  1. Access Token: 보호된 정보들에 접근할 수 있는 권한 부여에 사용된다.
  2. Refresh Token: 액세스 토큰의 유효 기간 만료 시 새로운 액세스 토큰을 발급한다.

  클라이언트가 처음 인증을 받을 때(로그인) 두 종류의 토큰을 다 받지만, 실제 권한을 얻는 데에 사용하는 토큰은 Access Token이다. 탈취될 수 있는 상황을 고려해서 짧은 유효 기간을 준다. Refresh Token마저 탈취되는 것을 막기 위해 Refresh Token을 사용하지 않는 곳이 많다.

만약 Refresh Token이 유출되어서 다른 사용자가 새로운 Access Token을 발급받았다면 충돌이 발생하기 때문에 서버에서는 두 토큰을 모두 폐기해야 한다.

 

🪙 구조

https://supertokens.com/blog/what-is-jwt / JSON 객체를 base64로 인코딩 한 형식

http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
  1. Header: 토큰의 종류(JWT)와 Sign을 하는 알고리즘 정보를 표기한다.
  2. Payload: 서버에서 활용할 수 있는 사용자 정보이다. base64로 인코딩 되기 때문에 디코딩이 용이하므로 민감한 정보는 담지 않는다. 토큰에 담긴 주체(Subject), 만료일(exp), 생성자(iss) 등을 담는다.
  3. Signature: 원하는 비밀키와 Header에서 지정한 알고리즘으로 Header와 Payload에 대해 단방향 암호화를 수행한다. 토큰의 위변조 유무를 검증하는 데에 사용한다.

 

🪙 인증 절차

  JWT는 권한 부여에 유용한다. 하나의 앱이 Gmail과 연동하여 이메일을 읽어 와야 한다고 가정해 보자. 사용자는 Gamil 서버에 로그인 정보를 제공하여 인증에 성공하면 JWT를 발급받는다. 앱은 이 JWT를 사용하여 사용자의 이메일을 사용할 수 있다.

  1. 클라이언트가 아이디와 패스워드를 입력하여 로그인 시도를 한다.
  2. 아이디와 비밀번호가 일치하는지 확인하고(DB에 접근), Secret Key를 통해 암호화 된 토큰을 생성한다.
  3. 클라이언트는 토큰을 저장한다. (Local Storage, Seession Storage, Cookie 등이 될 수 있다.)
  4. 클라이언트가 HTTP Header(Authorization Header) 또는 쿠키에 토큰을 담아 request를 전송한다.
    → Bearer authentication을 이용한다. 링크1 링크2
  5. 서버는 Signature를 체크하고 Payload로부터 클라이언트 정보를 확인하여 응답을 보내 준다.

 

🪙 Refresh Token이 탈취된다면?

  사용자 인증 정보를 담고 있는 Access Token은 보안 취약으로 인해 노출될 수 있는 상황에 대비하여 Refresh Token을 활용하는 방법을 택하여 Access Token의 유효 기간을 짧게 부여한다.

만약 Refresh Token가 탈취된다면 어떻게 해야 할까? 공격자는 이를 활용하여 Access Token을 발급받아서 정상적인 사용자처럼 위장할 수 있다. 이를 방지하기 위해 서버 측에서 검증 로직이 필요하다.

  • DB에 사용자과 1:1로 매핑되는 Access Token과 Refresh Token 쌍을 저장한다.
    → 이때의 DB는 빠른 속도가 필요하고 키-쌍 값을 다루므로 NoSQL이 권장된다.
  • 정상적인 사용자: Access Token으로 접근
    서버: DB에 저장된 Access Token과 비교, 검증
  • 공격자: 탈취한 Refresh Token으로 새로운 Access Token 생성
    서버: DB에 저장된 Access Token과 비교, 검증!!! 이때, 다른 것을 확인한다.
  • DB의 Access Token이 만료되지 않았다면 새로 발급받을 이유가 없다. 서버에서는 이를 Refresh Token이 탈취된 상황으로 인식하고 두 토큰을 만료시킨다.

 


📚 Refrence

댓글