Token, JWT Token ( Access / Refresh ) , 블랙리스트 / 화이트리스트

TopOfTheHead·2025년 11월 20일

Spring Security

목록 보기
11/27

프론트 + 백엔드 ( React + Spring )에서는 세션 인증 방식으로 통신이 어렵다.

세션 기반 인증 방식서버에 정보를 요청 및 응답할 때마다 JSessionId 쿠키를 포함하고, 해당 쿠키세션 ID를 저장 및 HTTPS를 통해 암호화되어 프론트엔드에서 사용이 어려운 단점이 존재.
▶ 매번 요청마다 프론트엔드에서 JSessionId요청 헤더에 포함 및 서버에서 해당 세션 ID를 확인하여 In Memory DB에서 확인해야하므로 비효율적

따라서, React + Spring 등의 통신 방식은 주로 JWT 토큰을 사용한다.

토큰 ( token )
Client-side에서 서버로 접근가능한 Key 같은 인증정보를 보관하는 방법.
서버가 아닌, 인증정보를 client-side에 암호화하여 저장.

서버에서 토큰의 발급만 수행하고 저장 & 관리하지 않아서 부하가 적다.
세션방식에 반해 분산환경에서 유리

HTTP Servcerstateless 특징으로 상대를 알 수 없다.
사용자는 매번 요청Authorization Header토큰을 포함하고, 서버는 전달된 토큰이 해당 서버에서 발급했는지 판단하기 위해 검증을 수행

  • 토큰문자열로서, 생성 시 사용자에 의해 임의만료가 불가능
    。오직 만료기간이 경과해야 만료되는 단점이 존재.
    수명일주일 동안 지속되는 Refresh Token은 해당 문제에 치명적.

    。따라서, 화이트리스트 / 블랙리스트 방식을 통해 해당 JWT 토큰을 등록하여 서버쪽에서 해당 토큰로그인명시적으로 차단하는 로직을 작성해야함.

JSON 토큰 ( JWT : Json Web Token ) :
에서 Authentication과 정보교환 등을 수행하기위해 사용하는 Token 기반 인증 방식
비대칭키 암호화 : RSA 방식으로 암호화하여 공개키비밀키가 필요

JWT유효기간만료될때까지 유지되며 빠르게 해제가 불가능.
Access Token / Refresh Token 방식을 통해 토큰수명을 짧게 관리함으로써 보안을 향상

클라이언트가 로그인 후 서버클라이언트에게 발급하여 쿠키 / 리디렉션으로 제공
▶ 이후 클라이언트에서 전송하는 HTTP Request마다 Authorization HeaderJWT를 포함하여 인증을 수행.

클라이언트Token을 보관하면서 Stateless 특징을 지니므로, Session이 필요없다.
▶ 또한 Session처럼 DB 조회없이 Token만으로 Authenticate가 가능.

JWT

  • 서버에서 JWT 토큰 발급Cookie로, 클라이언트에서 서버 요청Authentication HeaderBearer {token} 형식으로 지정

  • JWT 토큰Base 64인코딩되어있어 복호화가 가능
    토큰을 타인이 쉽게 복호화가 가능하므로, payload민감정보를 넣으면 안된다.

    。오직 JWT 토큰SignatureSecret Key를 통해 암호화가 되어있음.

  • JWT 토큰서명을 생성하기 위해서 Secret Key를 필수로 요구
    。해당 Secret Key를 기반으로 RSA 알고리즘 기반으로 토큰서명을 수행

    Secret Key 설정 시 최소한 8 Byte 이상이어야한다.
    비대칭키 ( RS256 , ES256 ) 등은 최소 2048bit의 사용을 권장하므로 KeyPairGenerator에서 문자열 key 크기2048 bit = 8 Byte으로 생성하도록 설정.
    레인보우 테이블에 의해 하나씩 브루트포스 되는것을 막고자 UUID 2개를 생성 및 조합 후 Base64인코딩문자열Key로 설정
    UUID Generator, Base64 Encoder

    。외부로 탈취되면 안되므로, application.ymlplaceholder로 등록하는 환경변수 ( .env )로 저장
    어플리케이션에는 @Value 또는 @ConfiguationProperties로 가져올 수 있음.
custom:
  jwt:
    secrets:
      app-key: ${base64UuidKey}

general or secrets 등의 명칭으로 관리하는게 좋다.

JWT 토큰 종류

  • Access Token
    짧은 유효기간 : 30분

    Access Token만료시간이 짧은 경우 유출되더라도 피해를 최소화하여 보안성이 좋지만, 자주 로그인해야하는 불편함이 존재
    Access Token유효기간을 짧게 구축하되 만료Refresh Token를 기반으로 새로운 Access Token을 발급하는 방식으로 구현
    사용자Access Token이 만료되더라도 다시 로그인할 필요가 없다.

    Access Token프론트엔드 단에서 Axios API인터셉터를 통해 인증 헤더만료된 토큰을 교환하도록 로직을 작성.
    ▶ 만약 요청 전송 직전Access Token유효기간을 먼저 확인 후 3분 이내 인 경우 Access Token을 재발급

  • Refresh Token
    Access Token보다 긴 유효기간 ( 일주일 )을 가진 재발급 용도 토큰
    만료 시 로그인을 다시해야함

    서버에서 사용되어 Access Token만료된 경우 재발급하는 역할
    클라이언트( = 프론트엔드 )에서 직접 다룰 일이 없으므로 보통 HttpOnly 쿠키에 저장

    Refresh Token갱신 용도 토큰으로서, 식별용 ID를 제외한 내부 Payload 정보가 필요 없음.
    최소 정보를 포함하도록 Access Token에 비해 많은 정보를 포함하지 않도록 설정.

    로그아웃Refresh Token블랙리스트 / 화이트리스트로 등록하여 해당 토큰을 통한 로그인 차단
    Token은 직접 만료가 불가능하므로, Refresh Token이 오래 살아있으면 해당 기간동안 무한 로그인이 발생

토큰인증 관리 전략
。블랙리스트 방식규모가 큰 프로젝트/화이트리스트 방식규모가 작은 프로젝트에서 사용 ▶ 블랙리스트 & 화이트리스트`를 둘다 혼합해서 사용이 가능.

  • 블랙리스트
    블랙리스트 테이블로그아웃 할 사용자토큰입력함으로써 해당 토큰을 통한 로그인을 차단하는 방식

    블랙리스트는 주로 RDBMS테이블 상에서 관리
    Redis 등의 인메모리 DB에서 관리 시 휘발성이 매우 크기 때문에, 로그아웃토큰 정보를 잃은 후 해당 토큰으로 재로그인하는 보안 위험이 존재

    블랙리스트 테이블의 경우 토큰 외에도 로그아웃 시점IP주소, IP주소를 통한 로그아웃 장소 위치를 기록가능

  • 화이트리스트
    화이트리스트 테이블로그인 중 인 사용자토큰입력하는 방식
    로그아웃 시 해당 테이블에서 토큰을 삭제

    화이트리스트의 경우 주로 Redis에서 로그인 을 관리
    로그인RedisRefresh Token을 저장하므로, 화이트리스트 방식으로서 로그아웃 시 해당 Redis에서 해당 Refresh Token을 삭제

    인메모리 DB이기에, 로그인 중인 사용자 토큰 정보를 잃어도, 해당 사용자는 다시 로그인을 수행해서 발급받으면 되므로 보안위협이 약함.

Header
서명 알고리즘에 대한 정보를 제공

typ( Token Type )과 alg( Signature Algorithm )로 구성

{
  "typ": "JWT", // 토큰 타입
  "alg": "HS256" // 서명 알고리즘 (HMAC-SHA256)
}

Base64인코딩되어 포함

Payload
JWTClaims를 포함하는 부분
노출되도 상관없는 에 해당

{
  "iss": "example.com",
  "sub": "user123",
  "aud": "myapp",
  "exp": 1716239022,
  "iat": 1716235422,
  "role": "admin" // private claim
}

Base64인코딩되어 포함

iss : ( issuer ) : 발급자
ex ) assetbox

sub : ( subject ): 주제
ex ) access-token

aud : ( audiance ) : 대상자
ex ) assetbox-user

exp : ( expiration ) : 토큰만료시간

iat ( Issued at ) : 토큰발급시간

jjwt를 통한 토큰 생성 시

서명 : Signature
서버의 특정 문자열 Key를 기반으로 서명알고리즘 ( RSA 등 )을 통해 단방향 알고리즘 처리한 서명

토큰유효성 검증 시 사용하는 고유한 서명
JWTHeader.Payload를 서명 후 Base64인코딩하여 포함

서명에 사용되는 Private Key는 보안을 위해 특정길이 이상을 사용하도록 강제
비대칭키 ( RS256 , ES256 ) 등은 최소 2048bit의 사용을 권장하므로 KeyPairGenerator에서 문자열 key 크기2048 bit = 8 Byte으로 생성하도록 설정.

위조 여부는 JWTSignature를 기반으로 해당 서버에서 도출된 서명인지 판단.
서명복호화가 불가능하고, 해싱을 통해 동일한 문자열을 입력해도 항상 해시값이 변화하므로 노출되도 안전.

Signature 생성과정

1. Header , Payload를 각각 Base64인코딩

2. 인코딩된 두 문자열.으로 결합
인코딩된Header.인코딩된Payload

3. 지정된 알고리즘Key서명을 수행하여 서명 Byte값을 생성
ex ) RS256 : SHA256으로 private key를 활용하여 서명

4. 서명 후 생성된 byte값base64인코딩하여 Signature 생성
JWT = Header.Payload.SignatureSignature이 된다.

Signature 검증과정

1. 클라이언트로 부터 수신한 JWT에서 Header, Payload , Signature 분리
。해당 Header, Payload, Signature은 각각 Base64Url인코딩된 상태

2. Header디코딩 후 얻은 JSON에서 서명 알고리즘 ( alg )에서 허용된 알고리즘인지 검증

3. Signature디코딩 후 얻은 서명 Byte값서버발급자Public Key로 검증

4. 검증payload의 신뢰여부를 결정
。실패 시 토큰을 거부

서버에서 Access Token / Refresh Token클라이언트에게 응답하는 방법


1. Access TokenResponse Body / Refresh TokenHttpOnlySecure flagf가 적용된 Cookie에 저장 후 클라이언트에게 응답
쿠키 방식


2. URLQuery ParameterAccessToken / Refresh Token을 포함 후 Redirection을 통해 응답
。해당 방식을 사용하는 경우 클라이언트가 아닌, 프론트엔드=내장 Express 서버로 반환하면서 보호됨

리디렉션 방식
예시 .

  http://localhost:3000/auth?
  access=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwicm9sZSI6IuqwkjIiLCJpYXQiOjE3Nzk3NTg0MjksImV4cCI6MTc3OTc2MDIyOX0.cy-HFQUXCDD-dyXpeOOPu07uTisUGaW99-oMVgC5clFPGyesVatm9-aKWkDnr729aIU1VFQUQL1j_g_1bOrPWA
  &
  refresh=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwicm9sZSI6IuqwkjIiLCJpYXQiOjE3Nzk3NTg0MjksImV4cCI6MTc4MDM2MzIyOX0.SXgYJD9AZNkime65UGFPoNHadv_wkPuY8K5P6kSAlo0gsoo7XfjY_a9ZhItQBrGR-rCui-Go1SVN7urHKIxpEA
profile
공부기록 블로그

0개의 댓글