본문 바로가기
스프링/security

스프링 시큐리티 JWT란 무엇일까? 왜 사용할까?

by 진믈리 2024. 9. 11.
예약 도메인 프로젝트를 진행하면서 로그인 로직을 JWT 토큰을 사용해보자는 계획을 세웠다. 그러나 프로젝트를 진행하면서 많은 의문점이 생겼다.
도대체 JWT를 왜 사용하는 것일까?

 

 

Json Web Token  

JWT 는 Json Web Token의 약자이다. 개발 공부를 어느정도 진행하다 보면 Json과 Web은 지겹도록 들어보거나 사용해보았을 것이다.하지만 Token 은 Json과 Web보다는 조금 덜 익숙했다.

 

하지만 몇번 사용해 본 적이 있다. 그것은 바로 Git 이다. 

Git을 로컬에서 사용할때 인증 방식을 아이디와 비밀번호 방식에서 토큰 방식으로 변경되었다. 토큰 인증방식이 어떤 장점이 있길래? 

Token이란???

Git에서 토큰 인증을 해본 경험이 있는 사람은 그래도 토큰이 인증방식에 사용되는거구나 라고 막연히 생각할 수 있을것이다.

로컬에서 Git을 인증할때 토큰을 발급받으면 이상한 알파벳 문자열을 생성받은 적이 있을것이다. 그 문자열이 바로 내 정보를 암호화 해서 로컬에서 사용하는 사람도 내가 맞습니다! 라는걸 인증해 주는 키가 되는 것이다.

 

이처럼 토큰이란 정보 또는 인증을 안전하게 전달하거나 저장하기 위해 사용되는 데이터 조각이다. 주로 보안 시스템에서 사용되며 사용자의 신원을 증명하거나 특정 권한을 부여하는 목적으로 사용된다. 토큰은 다양한 형태로 존재할 수 있으며 그 중에서 JSON Web Token(JWT) 와 같은 구조화 된 토큰은 널리 사용된다.

 

그럼 JWT 란?

JWT는 JSON 포맷으로 인코딩된 토큰으로 토큰 안에 인증 정보나 클레임을 담아 전송하는 방식이다. 서버는 JWT를 사용해 사용자의 신원을 검증하고 토큰에 포함 된 정보를 바탕으로 권한을 확인할 수 있다.

 

공식문서의 JWT 설명을 살펴보자  출처 - JWT 공식문서

JSON 웹 토큰(JWT) 은 JSON 객체로 당사자 간에 정보를 안전하게 전송하기 위한 컴팩트하고 독립적인 방식을 정의하는 개방형 표준(RFC 7519)입니다. 이 정보는 디지털로 서명되어 있기 때문에 검증하고 신뢰할 수 있습니다. JWT는 비밀(HMAC 알고리즘 사용) 또는 ECDSA를 사용하는 공개/비공개 키 쌍을 사용하여 서명할 수 있습니다.

 

JWT의 공식문서에 관한 설명이다. 설명을 확인해보면 JWT 토큰을 구현하는 주된 이유는 보안인것 같았다. 정말 보안적으로 안전할까?

먼저 구조를 살펴보자

 

JWT의 구조

JWT 는 압축된 형태로 점(.) 으로 구분된 세 부분으로 구성되며, 각 부분은 다음과 같다.

  • Header
  • Payload 
  • Signature

따라서 JWT는 일반적으로 xxxxx.yyyyyy.zzzzz 이런 형태를 가진다.

 

Header

헤더는 일반적으로 두 부분으로 구성된다. 토큰 유형인 JWT와 사용되는 서명 알고리즘(HMACSHA256 또는 RSA 등)이다.

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

그런 다음, 이 JSON은 Bsase64Url 로 인코딩 되어 JWT의 첫 번째 부분을 형성한다.

 

payload

토큰의 두 번째 부분은 클레임(토큰에 포함된 정보나 데이터)을 포함하는 페이로드 이다. 클레임은 사용자나 시스템에 대한 정보를 담고 있으며 토큰을 사용할 때 서버가 이 정보를 검증하고 해당 권한을 확인하는 데 사용된다.

  • Registered Claims(등록된 클레임)  
    • 이는 필수는 아니지만 권장되는 사전 정의된 클레임 세트로, 유용하고 상호 운용 가능한 클레임 세트를 제공한다. 일부는 다음 과 같다. iss(토큰 발행자), exp(토큰 만료 시간), sub(주제), aud(청중) 및 기타.
    • JWT는 압축적이기 때문에 클레임 이름은 세 글자로만 구성된다.
  • Public Claims(공개 클레임)
    • JWT를 사용하는 사람들이 자유롭게 정의할 수 있다. 하지만 충돌을 피하기 위해 IANA JSON 웹 토큰 레지스트리 정의 하거나 충돌 방지 네임스페이스를 포함하는 URI로 정의 해야한다.
  • Private Claims(비공개 클레임)
    • 발행자와 소비자 간의 동의 하에 사용되는 클레임으로 예를 들어 특정 사용자나 그룹에 대한 사용자 정의 정보가 여기에 포함될 수 있다.
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

그런 다음 페이로드는 Base64Url 로 인코딩 되어 JSON 웹 토근의 두번째 부분을 형성한다.

Signature

서명 부분은 토큰의 무결성과 신뢰성을 보장하는 역할을 한다. 서명은 토큰이 위조되지 않았음을 확인하기 위한 요소이다.

JWT의 세번째 부분으로 헤더와 페이로드를 기반으로 생성된다.

 

  • 헤더 와 페이로드를 가져와서 각각 Base64 방식으로 인코딩 한다.
  • 두 값을 결합한 문자열에 암호화 알고리즘을 사용하여 서명을 만든다. 이때 헤더에서 지정한 암호화 알고리즘을 사용하여 서명한다.
  • 서명 시 서버가 비밀 키 또는 공개 키 를 사용하여 암호화 한다.

서명은 메시지가 전송 도중 변경되지 않았는지 확인하는 데 사용되며, 개인 키로 서명된 토큰의 경우 JWT를 보낸 사람이 주장한 대로 본인인지 확인할 수도 있다.

사용시 주의 점

  • 내부 정보를 단순 Base64 방식으로 인코딩하기 때문에 외부에서 쉽게 디코딩 할 수 있다.
  • 외부에서 열람해도 되는 정보를 담아야 하며, 토큰 자체의 발급처를 확인하기 위해서 사용한다.

 

내가 생각한 의문점들

외부에서 쉽게 디코딩 되는 거면 보안에 취약한거 아니야?

  • JWT가 Base64Url로 인코딩 되어 외부에서 쉽게 디코딩 된다는 점은 사실이지만 JWT의 보안성은 인코딩에 있지 않고 서명(Signature)에 있다.
    • 만약 Role_USER 를 Role_ADMIN으로 변경하여 ADMIN 페이지에 접근하려 한다고 해보자 관리자 페이지에 접근이 가능할까? 불가능하다. 서명(Signature)이 달라지기 때문이다. 토큰의 헤더나 페이로드가 변경되면 서명이 달라지기 때문에 서버는 서명을 검증하고 변조된 토큰을 거부하는 것이다.

      그럼 또 다른 의문점이 생긴다.

그럼 유출된 토큰을 변경 없이 그대로 사용한다면?

  • 유출된 토큰은 만료 기간이 끝나기 전까지 악의적인 사용자가 그대로 사용할 수 있다. 이는 JWT의 큰 보안 취약점 중 하나일 수 있다. 토큰이 서명 은 무결성을 보장하지만 유출된 토큰 자체는 유효한 상태이기 때문이다.

    몇가지 해결책을 찾아보았다. 
    • 첫째 토큰 만료 시간을 짧게 설정한다.
      • 그렇다면 만약 토큰이 유출된다고 해도 만료시간이 짧기에 오래 사용하지 못한다.
      • 그럼 더 잦은 주기로 탈취될 위험이 더 큰것이 아닌가?
    • 리프레시 토큰 사용
      •  리프레시 토큰은 일반 엑세스 토큰보다 긴 만료 시간을 갖고 있으며 액세스 토큰이 만료되면 리프레시 토큰을 통해 새로운 액세스 토큰을 발급받는다.
      •  리프레시 토큰이 유출되었을 떄는 더큰 위험이 따르지만 이를 주기적으로 검증하거나 무효화하는 방식을 도입할 수 있다. 예를 들어 리프레시 토큰을 Redis와 같은 저장소에 저장하고 로그아웃하거나 유출된 토큰들은 Redis 서버에서 제거하는 것이다.

취약점의 몇가지 해결점을 찾았지만 그럼에도 불구하고 JWT를 사용해야 하는 이유를 완벽하게 납득하지 못했다. 

 

그럼 토큰 인증방식이 세션 로그인 방식보다 좋은점이 뭐야?

1. 민감한 정보를 담고있지 않다.

  • 비밀번호같은 민감한 정보를 담지 않고 사용자가 로그인할 때 발급받은 서명된 토큰을 사용하므로 서버 측에서는 민감한 정보를 계속 보관할 필요가 없다. 따라서 세션과 달리 유출된다고 하여도 민감한 정보까지 탈취당할 위험이 적다는 것이다.

2. 토큰 기반 인증의 자율성

  • JWT는 토큰 자체적으로 사용자의 인증정보(사용자 ID, 권한 정보 등) 를 포함할 수 있다. 따라서 서버는 데이터 베이스에 다시 접근하지 않고도 토큰 자체만으로 인증 정보를 확인할 수 있다. 또한 토큰의 유효 기간을 설정할 수 있어 토큰이 유효할 동안 인증 상태를 유지한다. 즉 비밀번호를 매번 재확인할 필요 없이 토큰이 만료되지  않는 한 재인증 할 필요가 없다.
    따라서 장기간 동안 로그인 상태를 유지하려고 세션 설정을 할 필요가 없기에 서버측 부하를 줄일수 있다.

3. 확장성

  • JWT는 다양한 클라이언트(모바일, 웹, 데스크탑 등) 에서 동일한 방식으로 사용할 수 있다. JSON은 대부분의 프로그래밍 언어에서 쉽게 파싱 할 수 있는 표준 포맷이다.

4.비밀번호가 없는 인증관계

  • JWT는 OAuth2 등과 같이 비밀번호를 사용하지 않는 인증 방식과 결합하여 활용될 수 있다. 예를 들어 OAuth2로 구글 계정을 통해 인증 한후 JWT를 발급하여 지속적인 인증을 유지할 수 있다는 것이다. 이 경우 비밀번호 없이 안전하게 인증 상태를 관리할 수 있다.

5. 권한 관리의 용이성

  • JWT에 사용자의 권한 정보를 포함할 수 있기에 각 요청에서 해당 사용자가 특정 API에 접근 가능한 권한인지 서버가 간단히 검증 할 수 있다.

 

이제 왜 JWT를 사용하는지 조금 이해했을것이다.
나는 이번 예약 도메인 프로젝트를 진행하며 인증 인가 에 대한 구분, OAuth2 구현, 대용량 트래픽에 대한 처리를 계획하고 있다. 현재 진행하는 프로젝트에서는 세션기반의 인증방식보다는 JWT기반 인증방식을 선택하는게 많은 부분에서 이점이 있을것같다.

 

 

다음글에서는 JWT 동작원리에 대해 알아보자

2024.09.12 - [스프링/security] - JWT의 동작원리

'스프링 > security' 카테고리의 다른 글

JWT의 동작원리  (0) 2024.09.12
메서드 권한 설정  (0) 2024.08.09
시큐리티를 활용한 권한 계층구조 설정  (0) 2024.08.08