본문 바로가기

Language/Spring

[JWT] JWT 등장 배경, Access Token, Refresh Token

Cookie와 Session의 문제점

Cookie의 문제점
  • 보안에 취약
    • 요청 시 쿠키의 값을 그대로 전송
    • 유출 및 조작의 위험성
  • 용량 제한으로 많은 정보를 담을 수 없음 (도메인 별 약 4KB)
  • 웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 다른 도메인 간 공유가 불가능
  • 쿠키의 사이즈가 커질수록 네트워크에 부하 증가

 

Cookie & Session의 문제점
  • 상태 유지(Stateful)
    • 서버는 각 사용자의 세션을 메모리나 데이터베이스에 저장
  • 쿠키를 포함한 요청이 외부에 노출되더라도 세션 ID 자체는 유의미한 개인 정보를 담지 않지만, 해커가 이를 중간에 탈취하여 클라이언트인척 위장할 수 있다는 한계가 존재
    → 세션 하이재킹(Session Hijacking)
  • 서버에서 세션 저장소를 사용하므로 요청이 많아지면 서버에 부하가 증가
  • 확장성 낮음
    • Scale-up이 아닌 Scale-out 방식으로 서버를 증설하게 되면, 해당 Session을 발급한 서버에서만 인증 검사가 가능하여 Hash 방식을 통해 해당 서버와만 연결을 하는 제약 조건이 있음
      → 해결책 : 세션 스토리지를 중앙화 (Redis 분산 캐시 등)하거나 JWT를 사용하여 Stateless 인증을 구현
  • Cross-Origin 문제
    • 클라이언트와 서버가 서로 다른 도메인을 사용할 경우, 세션 기반 인증은 설정이 복잡해지거나 제한적

JWT(JSON Web Token)

JWT의 등장
  • Stateless(무상태)한 인증 방식이 필요해졌고, 위의 Cookie와 Session의 문제점들을 보완하기 위해 JWT가 등장
  • JSON 객체로 당사자 간에 정보를 안전하게 전송하기 위한 컴팩트하고 독립적인 방식을 정의하는 개방형 표준(RFC 7519)
    자체적으로 서명이 포함되어 있어 신뢰성을 제공
JWT의 용도 (공식문서)
  • 권한 부여 (Authorization)
    • JWT를 사용하는 가장 일반적인 시나리오
    • 사용자가 로그인하면 이후의 매 요청마다 JWT가 포함되어 사용자가 해당 토큰으로 허용된 경로, 서비스 및 리소스에 액세스 가능
    • Single Sign On은 오버헤드가 작고 여러 도메인에서 쉽게 사용 가능
  • 정보 교환 (Infomation Exchange)
    • JSON 웹 토큰은 당사자 간에 정보를 안전하게 전송하는 좋은 방법
    • JWT는 서명될 수 있기 때문에(예: 공개/비공개 키 쌍 사용) 발신자가 자신이 말하는 사람인지 확인 가능
    • 또한 서명은 헤더와 페이로드를 사용하여 계산되므로 콘텐츠가 변조되지 않았는지 확인 가능

JWT 장단점은 이전 글에서 다뤘기에 다루지 않겠음

 

Access Token과 Refresh Token
Access Token

 

  • 역할
    • 클라이언트가 서버에 요청을 보낼 때, 해당 요청이 인증된 사용자로부터 온 것임을 증명하는 데 사용
    • 일반적으로 HTTP 헤더의 Authorization: Bearer <Access Token> 형식으로 전달
  • 유효 기간
    • 보통 짧게 설정(몇 분에서 몇 시간)
    • 만료 시간이 짧기 때문에, 토큰이 탈취되더라도 피해를 최소화 가능
  • 사용 예시
    • API 요청 시, 사용자 권한을 확인하거나 자원에 접근 권한을 부여할 때 사용

 

Refresh Token

 

  • 역할
    • Access Token이 만료되었을 때 새로운 Access Token을 발급받기 위해 사용
    • Refresh Token을 서버에 보내면 서버는 새로운 Access Token을 발급해 클라이언트에게 전달
  • 유효 기간
    • 보통 길게 설정(며칠에서 몇 달)동안 유효
    • Refresh Token은 서버에서 관리되며, 탈취될 경우 큰 보안 위협이 될 수 있기 때문에 보호에 주의가 필요
  • 보관 위치
    • Refresh Token은 보통 서버에서만 관리되거나, 클라이언트에 안전하게 저장(예: HttpOnly 쿠키)

JWT의 보안 고려 사항
  • 토큰 탈취 방지
    • JWT는 암호화되지 않은 Payload를 사용하므로, 민감한 정보는 담지 않아야 함
    • Access Token은 만료 기간을 짧게 설정하고 Refresh Token은 안전한 저장소에 저장
  • 토큰 무효화
    • 이미 발급된 JWT만료 전까지는 무효화가 불가하다는  단점을 가짐
    • 이를 해결하기 위해 서버에서 블랙리스트를 유지하거나 토큰 갱신 시 주기적으로 검증하는 방법이 존재

 

JWT 보안 전략
  • 짧은 만료기간 설정
    • 토큰 만료 시간을 짧게 설정하는 방법을 고려 가능
    • 토큰이 탈취되더라도 빠르게 만료되기 때문에 피해를 최소화 가능
    • 단, 사용자가 자주 로그인해야 하는 불편함이 발생
  • Refresh Token
    • 클라이언트가 로그인 요청을 보내면 서버는 Access Token보다 긴 만료 기간을 가진 Refresh Token을 발급하는 전략
      • 클라이언트는 Access Token이 만료되었을 때 Refresh Token을 사용하여 Access Token의 재발급을 요청
      • 서버는 DB에 저장된 Refresh Token과 비교하여 유효한 경우 새로운 Access Token을 발급하고, 만료된 경우 사용자에게 로그인을 요구
    • Refresh Token을  사용하면 Access Token의 만료 기한을 짧게 설정 가능
      사용자가 자주 로그인할 필요가 없음
    • 또한 서버가 강제로 Refresh Token을 만료시킬 수 있기 때문로그아웃 처리가 쉬움
    • 단, 검증을 위해 서버는 Refresh Token을 별도의 storage에 저장
      • 이는 추가적인 I/O 작업이 발생함을 의미하기 때문에 JWT의 장점(I/O 작업이 필요 없는 빠른 인증 처리)을 완벽하게 가져갈 수 없음
      • 따라서 해당 토큰의 저장위치에 대한 고민이 필요
    • 탈취 방지를 위해 Refresh Token을 보안이 유지되는 공간에 저장
  • Refresh Token 고려 사항
    • Refresh Token을 사용할 경우 서버 부하와 추가적인 I/O 작업이 발생하기 때문에 JWT의 간결함과 성능 향상 효과가 감소될 수 있음
    • 이로 인해 서버 자원 관리와 보안 사이의 트레이드오프 고려

 

토큰의 저장 위치
Access Token의 저장 위치
  • Local Storage
    • AccessToken은 5분에서 30분으로 탈취의 위험을 고려하여 매우 짧게 설정
      그럼에도 불구하고 탈취에 위험이 존재하므로 저장위치에 대한 고려가 필요
    • AccessToken은 JWT 를 통한 Stateless 특징의 핵심이기 때문에 클라이언트 Local Storage 에 저장 가능
    • 만료날짜 없이 데이터를 저장할 수 있어 브라우저를 닫거나 다시 열어도 데이터를 사용 가능
  • Local Storage 저장의 단점
    • JS 를 통해 localStorage 에 접근하면 바로 토큰에 접근 가능
    • 공격자가 악의적인 js 코드를 피해자 웹 브라우저에서 실행시키는 XSS 해킹 공격을 통해 해커의 악성 스트립트에 노출이 되어 토큰 탈취 가능
    • 스크립트 등 해킹에 사용될 수 있는 코딩에 사용되는 입출력값을 검증하여 무효화
      즉, XSS 공격에 취약점이 있을경우 JWT 만으로 부족
XSS(Cross Site Scripting)란?
게시판이나 웹 메일 등에 자바 스크립트와 같은 스크립트 코드를 삽입 해 개발자가 고려하지 않은 기능이 작동하게 하는 공격

URL 에 스크립트를 포함할 수 있으며, 게시물에 삽입 가능

이를 통해 XSS 공격 용도의 해당 페이지를 이용하는 사용자의 브라우저에 저장된 파일들을 탈취 가능
시스템 권한이나 악성코드 다운로드 등의 공격 가능
거짓 페이지를 노출하여 개인정보 유출문제 가능
XSS 공격은 IPS, IDS, 방화벽 등으로 방지가 안돼 문자 필터링 방식으로 공격을 방어
script 문자 필터링( 사용자의 모든 입력값에 대해 서버측에서 필터링을 하여 검증)
주로 스크립트를 실행하기 위한 특수문자를 필터링(<,>,",')

 

  • Cookie
    • 토큰 값을 서버에서 쿠키에 담아 전달하면 클라이언트는 해당 쿠키를 항상 서버에 전달하게 되고 해당 쿠키 값을 통해 인증, 인가 처리
    • Local Storage는 브라우저상 자바스크립트로 토큰 값에 접근이 가능
  • Cookie의 장점
    • 쿠키 설정 시 httpOnly 값을 활성화하여 네트워크 통신 상에만 토큰 정보가 들어있는 쿠키가 붙게 되어 JS로 토큰 값에 접근이 불가능해져 XSS 해킹 문제를 해결 가능
      단, XSS 공격으로부터 완전히 안전한 것은 아님
      • httpOnly 옵션으로 쿠키의 내용을 볼 수 없다 해도 js로 request를 보낼 수 있으므로 자동으로 request에 실리는 쿠키의 특성사용자의 컴퓨터에서 요청을 위조 가능
      • 즉, XSS가 뚫린다면 httpOnly cookie도 안전하지 않음
    • Secure 옵션을 통해 쿠키가 HTTPS 로만 전송되게 하여 보안 수준을 높일 수 있음
  • Cookie의 단점
    • 쿠키는 한정된 도메인에서만 사용
      • 해결방법 : 토큰이 필요할 시 현재 쿠키에 있는 토큰을 사용하여 새 토큰을 문자열로 받아오는 API 구현
    • CSRF 공격의 위험
      • 유저도 모르게 계정 탈퇴, 댓글 포스트 자동 작성, 회원정보 변경
      • 해결방법
        • HTTP Request Header 의 Referrer,Origin 체크
          헤더 조작이 가능해 완벽한 방어 불가
          즉, XSS 취약점이 있는경우 CSRF 공격에도 취약
        • CSRG 토큰 사용
          API 에 대한 HTTP 요청을 특정 함수를 통해서만 이루어지게 하는 방식 사용
        • Same Origin 정책으로 자바스크립트에서 타 도메인의 쿠키 값을 확인, 수정이 불가한 점을 활용
          즉, Cookie 설정에 SameSite 설정
          단, SameSite 속성을 쓰기 위해서 Secure 설정 적용
          • Strict
            • 가장 보수적인 정책
            • Strict로 설정된 쿠키는 크로스 사이트 요청에는 항상 전송되지 않음
            • 즉, 서드 파티 쿠키는 전송되지 않고 퍼스트 파티 쿠키만 전송
          • Lax
            • Strict에 비해 상대적으로 느슨한 정책
            • Lax로 설정된 경우 대체로 서드 파티 쿠키는 전송되지 않지만 몇 가지 예외적인 요청에는 전송
2020.2.4 구글 크롬(Google Chrome) 80버전으로 업데이트되면서 CSRF 공격을 막기 위해 크롬에 Cookie 정책에서 SameSite 속성의 기본 값이 "None"에서 "Lax"로 보안등급을 상향 조절

Lax등급에선 기본적으로 서로 다른 도메인에서는 Cookie 전송이 불가능하지만
HTTP get method / a href / link href와 같은 특수한 경우엔 쿠키를 전송
CSRF(Cross-Site Request Forgery) 공격은 사용자가 자신의 의지와 무관하게 공격자가 의도한 행위(수정,삭제,등록 등)를 특정 웹사이트에 요청하게 하는 공격

즉, 자신도 모르게 서버를 공격하는 경우
→ 계정정보를 탈취하는 것이 아니라 스크립트를 통해 사이트 외부에서 사이트의 API 를 사용하는 것처럼 모방 혹은 사이트 내부에서 스크립트가 실행되어 원하지 않는 작업 수행

즉, 서버에서 쿠키 설정httponly, samesite, secure 을 모두 설정하고 CORS 설정으로 해당 특정 도메인만 허용하여 보안적으로 안정화시킨다면 프론트서버에서 따로 토큰 관리할 필요없이 서버에서만의 토큰 관리를 통해 인증,인가 처리 가능

 

  • 추가 고려 사항
    • 쿠키와 CSRF 방지의 한계
      • CSRF 방지를 위해 SameSite 설정과 CSRF 토큰을 사용해도, XSS 공격이 성공한다면 여전히 위험이 존재
      • 따라서 XSS 방지를 위한 별도의 보안 조치(예: 콘텐츠 시큐리티 정책(CSP) 적용, 철저한 입력 검증 등)도 중요
    • 쿠키와 확장성
      • 서버 확장 시 세션이 아닌 JWT를 사용하더라도 쿠키 기반 인증은 로드 밸런싱과 확장성을 고려
      • 특정 서버에 의존하지 않도록 설정하는 것이 중요
Refresh Token 저장 위치
  • Refresh Token은 Access Token보다 훨씬 긴 만료 시간을 가지며, 탈취 시 보안 위협이 크기 때문에 일반적으로 서버 측에서 관리가 필요
  • Refresh Token이 노출되면 공격자가 Access Token을 계속 재발급받을 수 있기 때문에 저장 위치를 신중히 선택

 

  • DataBase(MySQL, H2...)
    • 데이터베이스에 저장하는것을 고려 가능하지만 Web Context에서 Repository를 사용하는 것은 권장되지 않음
    • Web Security 필터 또는 인터셉터에서 데이터베이스 접근을 피하는 것이 좋은 설계
    • 이는 주로 성능과 구조적 문제를 피하기 위한 것
  • Redis
    • 메모리 기반 데이터 저장소높은 성능과 빠른 응답 속도를 제공하기 때문인증 토큰과 같은 자주 접근하고 빠른 처리가 필요한 데이터를 저장하는 데 적합
    • Redis는 휘발성 메모리이므로 전원이 꺼지면 데이터가 소실될 수 있지만 Refresh Token은 재발급 가능하기 때문에 Redis의 휘발성 특성이 큰 문제가 되지 않음
    • Redis는 만료 기간을 설정할 수 있는 점도 Refresh Token 저장에 적합한 이유
    • Redis는 높은 성능을 제공하기 때문에 대규모 트래픽 처리에도 적합
    • 주로 자주 읽히거나 쓰이는 캐시 데이터나 인증 토큰을 저장하기에 적합하며, 만료 기한 설정 기능을 통해 자동으로 토큰을 관리 가능
    • 기본적으로 메모리에 데이터를 저장하지만, RDB (Redis Database)나 AOF (Append Only File) 방식을 통해 데이터 영속성을 보장 가능
      Refresh Token이 중요한 정보라면 이 기능을 활용해 데이터 손실을 방지 가능

정리
  • 데이터를 그대로 전송하는 Cookie에는 중요한 정보를 담을 수 없고 용량도 약 4KB로 제한
  • Session에 저장하는 것은 서버에 저장하는 것이기 때문에 Cookie에 비해 보안성이 높지만 서버의 부하가 증가하며, 세션 하이재킹을 통해 SessionID를 탈취하여 클라이언트인 척 위장이 가능
  • JWT를 사용하게 되면 서버에 저장된 Secret Key를 통해 토큰의 위변조를 확인할 수 있고 Client에 저장이 되기 때문서버의 부하가 감소될 뿐만 아니라 Stateless가 핵심
    이것이 현재 Access Token이라는 개념
  • Access Token이 탈취될 경우 손 쓸 방법이 없기 때문에 토큰 탈취 대책(XSS, CSRF 방어 등)이 중요
    만료 기간을 너무 짧게 설정하게 되면 사용자가 너무 빈번한 로그인을 해야 함
    → 이를 위해 Refresh Token이라는 개념이 추가
  • Refresh Token은 Access Token이 만료되었을 때 갱신(발급)하기 위해 사용이 되며, 보안상의 이유로 서버에 저장하는 것을 권장
  • 서버에 저장(RDBMS, Redis 등)되어있는 Refresh Token을 삭제하여 더 이상 Access Token을 갱신하지 못하도록 만들어 새롭게 로그인을 요청하게 할 수 있음 (강제 로그아웃)
  • Refresh Token을 서버에 저장하는 방식 JWT 첫 등장 시 클라이언트에 저장함으로써 Stateless를 유지하고 서버의 부담을 줄이는 목적성이 일부 훼손 되었지만, 보안과 유연성을 고려한 현실적인 선택

 

참고 자료

JWT Access Token, Refresh Token 활용 방법 : https://velog.io/@yhlee9753/JWT-accessToken-%EA%B3%BC-refreshToken-%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%99%9C%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C

 

JWT accessToken 과 refreshToken 을 어떻게 활용해야할까?

JSON Web Token (JWT) 은 웹표준 (RFC 7519) 으로서 두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인 (self-contained) 방식으로 정보를 안전성 있게 전달준다.자가 수용적(self-contained) : JWT 는 필요한 모

velog.io