본문 바로가기

카테고리 없음

[Spring Boot] Security6 JWT Token (2)

 

 

JWT 토큰에 대한 이해와 보안의 중요성

JWT 토큰의 세 번째 부분: 서명(Signature)

  • 서명은 JWT 토큰의 선택적인 부분입니다.
  • 내부 애플리케이션 간의 신뢰할 수 있는 통신에서는 서명이 필요하지 않을 수 있습니다.
  • 그러나 인터넷을 통해 클라이언트 애플리케이션과 JWT 토큰을 공유할 때는 서명이 필요합니다.
  • 이는 네트워크 상에서 토큰의 헤더와 페이로드가 조작되지 않았음을 보장합니다.

서명의 역할과 생성

  • 서명은 토큰이 생성된 후 데이터가 변경되지 않았음을 보장합니다.
  • SHA-256 같은 알고리즘을 사용하여 서명을 생성합니다.
  • 서명 생성 방법:
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)
  • 여기서 secret은 백엔드 애플리케이션에만 알려져 있는 값입니다.

서명 검증 과정

  1. 서명 생성:
    • 서버에서 JWT 토큰을 생성할 때, 헤더와 페이로드, 비밀 키를 이용해 서명을 생성합니다.
    • 이 서명은 Base64로 인코딩된 값입니다.
  2. 서명 검증:
    • 클라이언트가 요청을 보낼 때마다 서버는 헤더와 페이로드를 이용해 새로운 서명을 생성합니다.
    • 생성된 서명을 기존 서명과 비교합니다.
    • 두 서명이 일치하면, 토큰이 유효한 것으로 간주하고, 그렇지 않으면 조작된 것으로 간주합니다.

왜 JWT가 보안에 좋은지

  • 변조 방지: 서명이 있는 JWT 토큰은 헤더나 페이로드가 조작되었을 때 이를 감지할 수 있습니다.
  • 무결성 보장: 서명을 통해 토큰의 데이터가 변경되지 않았음을 보장합니다.
  • 인증된 출처: 서명을 통해 토큰의 출처를 검증할 수 있습니다.

 

JWT 토큰 생성 과정

  1. HeaderPayload를 각각 Base64 URL 인코딩합니다.
  2. 인코딩된 Header와 Payload를 점(.)으로 결합합니다.
  3. 결합된 문자열과 비밀 키(secret)를 사용해 서명을 생성합니다.
  4. 최종적으로 header.payload.signature 형식의 JWT 토큰을 생성합니다.

1. Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

이 JSON 객체를 Base64 URL 인코딩합니다:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

 

2. Payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

 

이 JSON 객체를 Base64 URL 인코딩합니다:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

 

3. Signature:

인코딩된 Header와 Payload를 점(.)으로 결합합니다:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

 

결합된 문자열과 비밀 키(secret)를 사용해 HMAC SHA-256 알고리즘으로 서명을 생성합니다:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  "your-256-bit-secret"
)

 

생성된 서명 예시

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

 

최종적으로 header.payload.signature 형식의 JWT 토큰을 생성합니다:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

 

 

JWT 토큰 검증 과정

 

클라이언트가 서버에 요청:

  • 클라이언트는 JWT 토큰을 Authorization 헤더에 담아 서버에 요청을 보냅니다:
     
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

 

서버에서 토큰 분리:

  • 서버는 토큰을 header.payload.signature로 분리합니다:
header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"
signature = "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

 

서명 재생성 및 비교:

  • 서버는 인코딩된 헤더와 페이로드를 사용해 새로운 서명을 생성합니다:
    plaintext
    코드 복사
    new_signature = HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), "your-256-bit-secret" )
  • 새로 생성된 서명 new_signature와 토큰에 포함된 서명 signature를 비교합니다.
  • 서명이 일치하면 토큰이 유효하다고 판단하고 요청을 처리합니다. 서명이 일치하지 않으면 토큰이 조작되었다고 판단하고 요청을 거부합니다.
new_signature = HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  "your-256-bit-secret"
)

요약

  • **비밀 키(secret)**는 서명을 생성하는 데 사용되며, 서버에서만 알고 있습니다.
  • 서명은 비밀 키와 함께 헤더와 페이로드를 인코딩하여 생성됩니다.
  • 클라이언트가 토큰을 조작하면, 서버에서 새로 생성한 서명과 토큰의 서명이 일치하지 않으므로, 서버는 이를 감지하고 유효하지 않은 토큰으로 처리합니다.