Access Token과 Refresh Token

yii·2025년 2월 12일

article

목록 보기
2/2
post-thumbnail

JWT 토큰

JWT 토큰이란 유저의 신원이나 권한을 결정하는 정보를 담고 있는 데이터다. JWT 토큰을 이용해 클라이언트와 서버는 안전하게 통신한다. 이유는 JWT 토큰 인증 방식은 비밀키로 암호화를 하기 때문이다. 그러나 이 토큰을 다른 사람이 탈취할 수 있고, 본 클라이언트와 탈취한 사람을 서버에서 구별할 수 없기 때문에 유효기간을 두어야 한다.

이때 유효기간을 짧게 두면 사용자가 로그인을 자주 해야한다는 단점이 있고, 길게 두면 보안상 탈취 위험이 있기 때문에 유효 기간이 다른 두 JWT 토큰 Access Token, Refresh Token을 두는 것이다.

Access Toekn의 유효 기간 ( 1시간, 60일)은 짧고, Refresh Token의 유효 기간(1년)은 길다. 평소 API 통신을 할 때는 Access Token을 사용하고, Refresh Token은 Access Token이 만료되어 갱신될 때만 사용한다. 즉 Refresh Token을 주기적으로 재발급함으로써 피해를 최소화하는 것이다.

JWT 토큰 구조

Header, Payload, Signature로 되어있다. 만료 기간은 Payload에 적혀있고, Signature은 Header+Payload를 비밀키로 암호화한 것이다.
header의 경우 토큰 자체를 설명하는 데이터를 담고 있는데, 얼마든지 디코딩 될 수 있기 때문에 민감한 정보를 담지 않아야 한다.
탈취자는 탈취한 토큰을 계속 사용하기 위해 Payload에 있는 만료 기간을 늘리려 할 것이다. 그러나, Payload의 만료 기간을 바꾼다 해서 Signature이 바뀌지 않는다. Signature에서 복호화한 Payload와 탈취자가 변경한 Payload가 일치하지 않는 것을 서버에서 알게 되면 접근 권한을 내주지 않게 된다.

Access Token이란?

서버에 접근하기 위해 axios 등 authorization header에 넣어주는 토큰이다.

Refresh Token이란?

서버에 접근하기 위한 토큰이 아닌, 액세스 토큰이 만료됐을 경우 access Token을 재발급 받기 위해 사용되는 토큰이다. 보통 만료시간이 더 길고 서버에서 관리한다.

Access Token과 Refresh Token의 저장 위치는?

local Storage와 session Storage, 쿠키 등에 저장할 수 있는데, 쿠키는 서버에서 읽을 수 있는 반면, localStorage는 브라우저에서만 읽을 수 있다. 또 쿠키는 4KB의 크기 제한을 가지고 있지만 localStorage는 더 많은 데이터를 저장 가능하다.

localStorage는 페이지를 이동하거나, 브라우저를 다시 시작해도 만료 없이 유지된다. JS를 사용하여 토큰에 액세스하기 때문에 편리하고, 콘텐츠를 자동으로 보낼 수 없기 때문에 CSRF 공격에 안전하다. 그러나 cross-site Scripting (XSS)에 취약하다. 탈취자가 악성 JavaScript를 웹 페이지에 주입할 경우 localStorage에 있는 Access Token을 탈취할 수 있다.

session Storage는 페이지를 새로고침하거나 이동해도 유지도지만 새로운 탭에서 접속 시 세션이 나뉘어지고, 브라우저가 종료되는 순간 휘발되어 사용자 경험에 좋지 않다. 모든 JS 코드로 액세스 할 수 있기 때문에 마찬가지로 XSS 공격에 취약하나, 자바스크립트 코드로 제어가 필요하기 때문에 CSRF 공격에는 안전하다.

Cookie의 경우 Security와 HttpOnly 옵션을 통해 XSS를 방어할 수 있다. 자바스크립트가 쿠키를 조작하는 것을 막을 수 있기 때문에 토큰이 탈취 당하거나 조작하는 것을 보호할 수 있다. 그리고 서버에 대한 모든 HTTP 요청에서 자동으로 전송된다. 애플리케이션에 JavaScript를 실행할 수 있는 경우 HTTP 요청을 보낼 수 있으며 자동으로 쿠키에 포함되기 때문에 CSRF에 대한 공격에 취약하다. 또, 쿠키 방식은 모든 요청에 쿠키가 함께 전송되기 때문에 성능 저하의 원인이 될 수 있다.

즉, 쿠키와 localStorage 모두 XSS 공격에 취약하지만 쿠키는 보안 옵션을 통해 이러한 취약점을 완화할 수 있다. 또 허용된 URL에서만 요청할 수 있게 설정해줄 수 있기 때문에 클라이언트에서 Authorization 헤더에 토큰 값을 일일히 넣어주어야 하는 번거로움을 막을 수 있다.

쿠키 플래그

  • secure
    브라우저에서 쿠키는 HTTP, HTTPS 프로토콜과 관계없이 서버에 전달되기 때문에 탈취자에게 쿠키 정보를 탈취당하 수 있다. secure을 명시하면 HTTPS 프로토콜로 전달하여 쿠키 정보를 보호할 수 있다.
  • httpOnly
    서버 응답으로 획득한 쿠키는 기본적으로 브라우저의 클라이언트(JavaScript)에서 읽고 수정하는 것이 가능하다. 이 경우 공격자로부터 탈취될 수 있어 위험하다. HttpOnly를 명시하면 클라이언트에서의 접근이 차단되어 보안을 강화할 수 있다.
  • SameSite
    Strict, Lax 모드가 있으며 Strict 모드에서는 같은 도메인 범위 내에서만 쿠키를 사용하게 하고, Lax 모드에서는 사용자가 페이지 이동 시 혹은 Form을 통한 Get 요청 시에만 허용된다.

XSS공격이란?

공격자가 사용자의 브라우저에 스크립트가 실행되도록 하여 사용자의 세션이나 웹 사이트를 변조하여 악의적인 콘텐츠/ 피싱 공격을 하는 것이다. 예를 들면 게시글 내용에 script 태그를 입력하여 게시글 조회 시 허용되지 않은 스크립트를 실행하는 것이다. 주로 백엔드 측에서 게시글 정보를 DB에 저장할 때 적절히 필터를 하지 않으면 XSS 공격이 발생한다.

CSRF 공격이란?

사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 행위이다.

서버-클라이언트 통신 과정

  1. 로그인에 성공한 클라이언트는 Refresh Token 과 Access Token 두 개를 서버로부터 받는다.
  2. 클라이언트는 Refresh Token과 Access Token을 로컬에 저장한다.
  3. 클라이언트는 헤더에 Access Token을 넣어 API 통신을 한다.
  4. Access Toekn의 유효기간이 만료되면
    • 사용자는 권한이 없는 사용자가 된다.
    • 클라이언트로부터 유효기간이 지난 Access Token을 받은 서버는 401 에러 코드로 응답한다.
    • 401을 통해 클라이언트 또한 invalid_token 을 알 수 있다.
  5. 헤더에 Access Token 대신 Refresh Token을 넣어 API를 재요청한다.
  6. Refresh Token 으로 사용자의 권한을 확인한 서버는 응답쿼리 헤더에 새로운 Access Token을 넣어 응답한다.
  7. 만약 Refresh Token도 만료되었을 경우 401 에러를 서버로부터 받게되고, 클라이언트는 이때 재로그인해야한다.

고민해본 방법

☝️ 프론트엔드에서 Access Token은 메모리(비공개 변수)에 저장하고, 백엔드에서 refresh token을 쿠키에 지정하여 httpOnly / secure / SameSite(Strict or Lax 모드) 옵션을 지정한다
☝️ 이때, 요청 시 쿠키에는 자바스크립트에서 접근 불가능한 (httpOnly 옵션) refresh Token이 담긴 채로 서버와 통신을 하게 된다.
☝️ 위와 같이 진행할 경우 refresh Token이 저장된 쿠키는 외부 경로와 자바스크립트 상에서의 접근이 불가능하여 CSRF, XSS 공격에서 안전성이 확보된다. access Token이 저장된 비공개 변수는 XSS, CSRF 공격을 시도할 방법이 사라지며, 토큰이 휘발되는 문제의 경우 상태 관리 라이브러리의 지속성 기능을 사용하여 해결할 수 있지 않을까 생각해보았다.

참고

https://velog.io/@chuu1019/Access-Token%EA%B3%BC-Refresh-Token%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C
https://velog.io/@tjseocld/AccessToken-%EB%A5%BC-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EB%B3%B4%EA%B4%80%ED%95%98%EA%B8%B0
https://matt1235.tistory.com/69
https://dreamcode.tistory.com/444
https://velog.io/@hyunjeong9592/JWT-Refresh-Token-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EB%B3%B4%EC%95%88-%EC%A0%84%EB%9E%B5

profile
프론트엔드 개발자

0개의 댓글