본문 바로가기
스프링/시큐리티

JWT(JSON Web Token): (이론) JWT 그리고 스프링 부트 적용하기

by limdae94 2024. 6. 3.

0. 시작하기 전에

강의 혹은 책에서 흔하게 JWT 토큰과 함께 OAuth2.0 을 함께 사용하여 네이버, 카카오, 구글, 애플 등 서드파티 애플리케이션에게 인증을 위임한다. 아쉽지만 현재 프로젝트에서는 OAuth2.0 을 적용한 인증 방식은 채택하지 않고, JWT 토큰 인증 방식만 채택하는 시나리오이다. 가볍게 토큰 인증 방식과 OAuth2.0 에 대해 살펴보자.

 

1. 헤더

 

포스트맨(Postman) 에서 요청을 확인하기 위해 HTTP Method, URL 을 입력하고 응답을 확인한다. 필요에 따라서 Params, Authorization, Body 를 작성하고 요청을 함께 보내게 되는데, 요청에 필요한 모든 정보를 포함한 것을 HTTP 메시지라고 한다. HTTP 메시지는 클라이언트와 서버 간의 통신에서 전송되는 데이터를 포함하며, 요청과 응답 모두에 사용된다. 이러한 메시지는 클라이언트에서 서버로의 요청과 서버에서 클라이언트로의 응답을 구성한다.

 

postman 으로 JWT 리프레시 토큰 재발급 요청하기

 

HTTP 메시지의 구조는 다양한 형식으로 표현될 수 있다. 주로 텍스트 형식인 MIME(Multipurpose Internet Mail Extensions) 형식을 사용한다. HTTP/1.1 부터는 메시지의 바디가 선택 사항이 되어 일부 요청이나 응답은 바디를 포함하지 않을 수도 있다. 헤더 안에 포함된 다양한 정보를 간단하게 확인하고 넘어가자. 추후에 HTTP 메시지 안의 정보와 HTTP Method 에 대해 자세히 정리할 것이다.

 

 

2. 인증

인증 흐름

 

인증(Authentication)은 시스템이나 애플리케이션에서 사용자가 자신의 신원을 증명하는 과정이다. 인증을 통하여 시스템 혹은 애플리케이션은 사용자의 신원이 실제로 해당 사용자인지를 확인한다. 인증 과정은 다음과 같이 이루어진다.

 

  1. 무엇을 알고 있는가 (What You Know): 비밀번호, PIN 번호
  2. 무엇을 가지고 있는가 (What You Have): 토큰, 인증 앱
  3. 무엇을 나타내는가 (What You Are): 생체 인식
  4. 어디에 있는가 (Where You Are): 위치 기반 인증

위의 네 가지 항목 기준으로 인증의 과정은 다음 단계를 거치게 된다.

  1. 사용자 입력: 아이디와 비밀번호 등의 인증 정보를 입력
  2. 인증 정보 확인: 애플리케이션은 인증 정보를 저장된 데이터와 비교하여 확인
  3. 인증 결과: 정확하면 사용자를 인증하고, 그렇지 않으면 접근을 거부

인증은 웹사이트 로그인, 이중 인증(2FA), 생체 인증 등으로 복합적인 인증 절차를 권장한다. 인증( Authentication)과 권한 부여(Authorization)는 밀접하게 관련되어 있지만, 두 개념은 서로 다르다. 인증은 자신의 신원을 증명하는 과정이며, 권한 부여은 인증된 사용자가 애플리케이션에서 어떠한 자원에 접근할 수 있는지를 결정하는 과정이다.

 

 

3. 토큰

토큰(Token)은 클라이언트 - 서버 간의 인증 및 권한 부여를 위해 사용되는 임의의 문자열이다. 토큰은 인증된 클라이언트에게 발급되며, 클라이언트는 이후의 요청에서 토큰을 사용하여 인증을 수행한다. 참고로 흔히 세션과 비교하여 여러 가지 장점을 갖고 있지만, 절대적인 우위를 갖는 것은 아니다. 

 

 

4. 토큰 인증 방식

토큰 인증 방식은 현대적인 웹 페이지 및 모바일 애플리케이션에서 널리 사용되는 인증 방식이다. 사용자가 인증에 성공하면 서버에서 액세스 토큰(access token)을 발급한다. 액세스 토큰은 사용자가 인증된 상태를 유지하면서 서버에 요청을 보낼 때 사용된다. 대표적인 토큰 인증 방식인 JWT(JSON Web Token)가 널리 사용된다. 토큰 인증 방식은 JWT 설명과 함께 아래에서 더 자세히 설명한다.

 

 

5. OAuth 2.0

OAuth 2.0 는 인증 및 권한 부여를 위한 프레임워크로, 특히 서드파티(3rd party) 애플리케이션에서 사용자의 데이터에 안전하게 접근할 수 있도록 위임하는 데 사용된다. 이를 통해 사용자 정보에 대한 접근 권한을 부여할 수 있으며, 이 과정에서 토큰(특히 액세스 토큰)을 사용한다.

 

OAuth 2.0 는 사용자가 자신의 자격 증명을 제공하지 않고도 다른 서비스(예: Google, Facebook 등)에서 제공하는 데이터를 애플리케이션이 접근할 수 있게 해준다.

 

 

6. 토큰 인증 방식 그리고 OAuth 2.0 의 통합

  • 단독 사용: 토큰 인증 방식을 단독으로 사용하면 사용자가 특정 서비스에 직접 로그인하여 토큰을 발급받고, 계속해서 인증을 유지한다.
  • 위임 사용: OAuth 2.0 을 사용하여 서드파티 애플리케이션에게 인증을 위임할 수 있다. 예를 들어 Google 계정을 사용하여 로그인하고, OAuth 2.0 를 통해 애플리케이션은 액세스 토큰을 받아 사용자의 데이터를 접근할 수 있게 된다.

 

7. 오늘날 인증 방식

토큰 인증 방식과 OAuth 2.0을 함께 사용하는 경우는 대부분 서비스에서 채택하고 있는 인증 방식이다. OAuth 2.0 은 특히 서드파티 애플리케이션에게 인증을 위임하는 시나리오에서 유용하며, 이 과정에서 액세스 토큰을 발급받아 사용하는 방식이 널리 채택되고 있다.

 

정리하자면, 토큰 인증 방식을 단독으로 사용하기보다는 OAuth 2.0 와 같은 프레임워크와 결합하여 서드파티 애플리케이션에게 인증을 위임하는 방식이 현대적인 웹 및 모바일 애플리케이션에서 일반적이다.

 


 

1. JWT

 

JWT(JSON Web Token)이란 인증에 필요한 정보들을 암호화시킨 JSON 형식을 갖는 토큰이다. 그리고 JWT 기반 인증은 JWT 토큰(Access Token)을 HTTP 헤더에 포함하여 서버가 클라이언트를 식별하는 방식이다. 쉽게 말하자면, 회원 인증 및 회원 인증을 유지하기 위한 토큰 방식 중 하나이다. 일반적으로 HTTP 헤더의 Authentication 안에 Bearer 문자열을 작성하고 한 칸 띄어쓰기 한 후에 JWT 토큰을  작성하여 함께 전송한다. 즉, Bearer {JWT 토큰} 형식으로 클라이언트로 전송한다. Bearer 에 대해서는 곧 아래에서 학습하자.

 

RFC 7519

 

국제 인터넷 표준화 기구 IETF(Internet Engineering Task Force) 기관에 의해 표준으로 정의된 RFC 7519 는 JWT 토큰의 구조, 사용 사례, 보안 고려사항 등을 상세히 설명하고 있다. 세 명의 주요 기여자에 의해 JWT 는 오늘날 등장했다.

  1. Michael B. Jones: 마이크로소프트의 기술 전문가. JWT 표준화를 주도한 인물 중 한 명
  2. John Bradley: 보안 및 아이덴티티 분야의 전문가. JWT 개발에 기여
  3. NAT Sakimura: 디지털 아이덴티티와 관련된 국제 표준 개발에 참여한 전문가.

JWT 기여자에 의해 작성된 문서가 궁금하다면, RFC7519 에서 30 페이지 가량의 공식 문서를 확인할 수 있다. 해당 문서 안의 내용을 바탕으로 스프링 부트에서 JWT 를 어떻게 활용할 수 있는지 자세히 정리할 것이다.

 

 

RFC 7519: JSON Web Token (JWT)

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JS

datatracker.ietf.org

 

 

1. JWT 구조

jwt.io

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

 

JWT 공식 홈페이지에서는 JWT 토큰 디코딩, 인코딩 그리고 각 언어마다 지원하는 JWT 라이브러리를 확인하고 해당 라이브러리의 깃허브 리포지토리로 이동할 수 있다. 메인 페이지에서 JWT 토큰 구조와 담긴 정보를 대략적으로 짐작할 수 있다. JWT 메인 페이지의 Encoded 아래 부분에는 세 개의 점으로 임의의 문자열이 존재하고, Decoded 아래 부분은 Header, Payload, Verify Signature 세 가지로 나눠진다. 각 구분된 JWT 토큰 구조에 대해 자세히 학습해보자.

 

 

JWT 구조

 

JWT 토큰은 . 구분자로 세 가지 임의의 문자열이 조합된 구조이다. 왼쪽부터 헤더(Header), 내용(Payload), 서명(Signature) 으로 구성된다. 임의의 문자열은 모두 JSON 데이터를 Base64 URL-safe Encode 를 통해 인코딩하여 직렬화한 것이며, 토큰 내부에는 위변조 방지를 위해 개인키를 통한 전자서명도 들어있다. 따라서 사용자가 JWT 를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치게 되며 검증이 완료되면 요청한 응답을 돌려준다.

 

위의 그림에서 세 개의 네모 칸 안에 JSON 데이터를 살펴보자. JSON 형식으로 작성된 내용이 Base64 URL-safe Encode 으로 임의의 문자열 세 가지으로 치환되어 . 구분자로 조합된 것이다. 이것이 바로 JWT 이다. 이제 JWT 안에 포함된 세 개의 정보에 대해 학습하자.

 

 

Header

Header

  • alg: 서명(Signature)에서 사용하는 암호화 알고리즘(ex: HMAC SHA256, RSA )
  • typ: 토큰 유형


서명(Signature) 에서 사용하는 알고리즘은 대표적으로 RS256(공개키, 개인키) 와 HS256(비밀키(대칭키))가 있다.  Auth0 by Okta 공식 문서에서 JWT 에서 사용하는 암호화 알고리즘을 자세히 설명해주고 있다. 해당 문서를 쉽게 이해하기 위해서는 대칭, 비대칭 암호화 알고리즘에 대해 선수 지식이 필요하다.

 

Auth0

Get started using Auth0. Implement authentication for any kind of application in minutes.

auth0.com

 

 

Payload

Payload

  • sub: 제목
  • name: 회원 이름
  • iat: 발행 시간

제목, 회원 이름, 발행 시간 뿐만 아니라 페이로드(Payload) 안에는 더 많은 데이터가 포함될 수 있는데, 페이로드는 정해진 형식과 데이터 타입이 없기 때문이다. 이처럼 페이로드 안에는 JWT 토큰을 통해서 알 수 있는 데이터가 포함되어 있으며 JSON 형식이므로 "키(Key)":"값(value)" 으로 데이터를 표현한다. "키(Key)":"값(value)" 의 데이터를 JWT 에서는 클레임(Claim) 이라고 부른다. 페이로드는 정해진 데이터 타입이 없지만, 대표적으로 Registered claims, Public claims, Private claims 이렇게 세 가지로 나뉜다.

 

 

1. 등록된 클레임(Registered claims)

 

토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들로, 모두 선택적으로 작성이 가능하며 가장 권장하는 방식이다. 또한 JWT 를 간결하게 하기 위해 Key 는 모두 길이가 3 의 String 타입이다. Key 중에서 subject 는 일반적으로 고유 식별 번호처럼 유일한 값으로 사용하는 것을 권장한다. 등록된 클레임에 포함된 데이터 정류는 다음과 같다.

  • iss (Issuer): 발행자
  • sub (Subject): ` 제목
  • aud (Audience): 대상자
  • exp (Expiration Time): 만료 시간
  • nbf (Not Before): 유효해지기 시작하는 시간
  • iat (Issued At): 발행된 시간
  • jti (JWT ID): 고유 식별자
{
  "iss": "https://example.com", // 발행자: 도메인 URL
  "sub": "user123", // 제목: 일반적으로 사용자 ID
  "aud": "https://example-audience.com", // 대상자
  "exp": 1715312845, // 만료 시간
  "nbf": 1615312445, // 유효 시작 시간
  "iat": 1615312445, // 발행된 시간
  "jti": "unique-jwt-id" // JWT 고유 식별자
}

 

 

2. 공개 클레임(Public claims)

 

공개 클레임은 등록되지 않았지만 공개적으로 정의된 클레임이다. 공개 클레임은 이름 충돌을 방지하기 위해 클레임 이름을 URI 형식으로 정의해야 한다. 예를 들어, 클레임을 정의하고자 하는 조직의 도메인을 포함하여 고유한 이름 공간을 사용할 수 있다.

{
  "iss": "https://example.com", // 발행자: 도메인 URL
  "sub": "user123", // 제목: 일반적으로 사용자 ID
  "aud": "https://example-audience.com", // 대상자
  "exp": 1715312845, // 만료 시간
  "custom_claim": "custom_value" // 애플리케이션에 특화된 사용자 정의 클레임. -> 공개 클레임
}

 

 

3. 비공개 클레임(Private claims)

 

비공개 클레임은 서버와 클라이언트 사이에 임의로 지정한 데이터를 저장하는 클레임이다. 표준에 등록되지 않고, 애플리케이션 특화 데이터를 포함할 수 있다.

{
  "iss": "https://example.com", // 발행자: 도메인 URL
  "sub": "user123", // 제목: 일반적으로 사용자 ID
  "aud": "https://example-audience.com", // 대상자
  "exp": 1715312845, // 만료 시간
  "department": "engineering", // 사용자가 속한 부서
  "role": "admin" // 사용자의 역할
}

 

위 예시에서 "department""role"은 비공개 클레임이다. 이 클레임들은 발행자, 수신자 두 당사자 간에만 의미가 있으며, 다른 시스템과의 충돌을 방지하기 위해 등록되지 않는다.

페이로드

 

위의 그림에서 iss, sub, iat, exp 는 공개 클레임이고, roles 는 비공개 클레임이다. 페이로드는 작성과 관련된 정해진 규칙이 전혀 없기 때문에 최소한의 정보를 포함하여 클라이언트에게 전달할 수 있으면 된다. 따라서 위의 그림처럼 필요한 정보만 페이로드 안에 포함하고 필요하다면 더 추가해도 된다.

 

 

Signature

 

Signature
Signature 조합

 

헤더에서 정의한 알고리즘(alg)으로 헤더와 페이로드 그리고 서버가 갖고 있는 비밀키를 합쳐서 암호화한다. 헤더와 페이로드는 단순히 인코딩된 값이기 때문에 제 3자에 의해 복호화 및 데이터 조작을 할 수 있지만, 서명(Signature)은 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다. 따라서 서명은 토큰의 위변조 여부를 확인하는데 사용된다. 추후에 실습을 통하여 쉽게 이해하자.

 

 

2. AccessToken, RefreshToken

JWT 토큰은 한 개만 발급하여 회원 인증을 유지할 수 있다. 하지만 일반적으로 회원 인증을 유지하기 위해 액세스 토큰(AccessToken)과 리프레시 토큰(RefreshToken) 두 개를 발급한다. JWT 토큰을 두 개로 사용하는 이유는 보안과 사용자 경험을 모두 최적화하기 위해서이다. 각각의 토큰은 서로 다른 목적과 수명을 가지며, 두 개를 함께 사용함으로써 여러 가지 이점을 제공한다.

 

1. 보안 강화

  • 액세스 토큰은 짧은 수명을 가지며, 사용자가 보호된 요청(리소스)에 접근할 때 사용된다. 만약 액세스 토큰이 탈취되더라도 짧은 시간 내에 만료되기 때문에 보안 위험이 줄어든다. 액세스 토큰의 만료 시간은 3분, 5분, 15분, 30분, 60분 등 다양하게 설정된다.
  • 리프레시 토큰은 긴 수명을 가지며, 새로운 액세스 토큰을 발급받기 위해 사용된다. 리프레시 토큰은 안전하게 저장되고, 클라이언트 측에서 액세스 토큰이 만료되었을 때만 사용되기 때문에 주로 백그라운드에서 사용되며 노출될 가능성이 낮다. 리프레시 토큰의 만료 시간은 7일, 14일, 30일 등 다양하게 설정된다.

 

2. 사용자 경험 개선

  • 액세스 토큰이 만료되더라도, 사용자는 리프레시 토큰을 사용해 새로운 액세스 토큰을 자동으로 발급받을 수 있다. 사용자가 로그아웃하거나 재로그인할 필요 없이 원활한 서비스 이용을 가능하게 한다.

 

3. 부하 분산

  • 액세스 토큰의 유효성을 매번 서버에서 확인할 필요가 없다. 서버는 짧은 수명의 액세스 토큰을 통해 클라이언트를 인증하고, 필요한 경우에만 리프레시 토큰을 통해 새로운 액세스 토큰을 발급한다. 이는 서버 부하를 줄이는 데 도움이 된다.

 

4. 세션 관리

  • 리프레시 토큰은 세션 관리의 역할을 대체 할 수 있다. 사용자가 로그아웃하거나 장기간 활동이 없을 경우 리프레시 토큰을 무효화하여 세션을 종료할 수 있다.

액세스 토큰과 리프레시 토큰을 함께 사용하는 방식은 보안과 사용자 경험을 동시에 고려한 설계이다. 짧은 수명의 액세스 토큰을 통해 보안을 강화하고, 긴 수명의 리프레시 토큰을 통해 사용자가 지속적으로 서비스를 이용할 수 있도록 한다. 이를 통해 시스템은 더 안전하고 효율적으로 작동할 수 있다.

 

 

3. JWT 흐름

JWT 토큰이 발급되는 시점은 크게 두 가지이다. 회원이 로그인에 성공하면 서버에서 액세스 토큰(AccessToken), 리프레시 토큰(RefreshToken) 두 개의 JWT 토큰을 발급하고, 액세스 토큰이 만료되면 서버에 요청하여 회원은 액세스 토큰과 리프레시 토큰을 재발급 받아서 회원 인증을 유지할 수 있다. 아래 그림을 통하여 구체적으로 어떻게 JWT 토큰이 발행되는지 학습한다.

 

액세스 토큰, 리프레시 토큰 발급과 재발급

 

0. 로그인에 성공하고 데이터 요청하는 경우

  1. [사용자] 회원이 로그인 요청(POST | /auth/login)을 한다.
  2. [서버] 로그인 요청받은 서버는 회원 인증 정보인 아이디와 비밀번호가 데이터베이스에 저장된 정보와 일치하는지 확인한다.
  3. [서버] 인증 정보가 일치한다면 액세스 토큰과 리프레시 토큰을 생성하고 응답한다.
  4. [사용자] 로컬 스토리지 등에 토큰을 보관하고 데이터 요청(GET | /member)마다 액세스 토큰을 포함하여 전송한다.
  5. [서버] 요청과 함께 포함된 액세스 토큰이 유효한지 검증한다.
  6. [서버] 액세스 토큰이 유효하다면 요청한 데이터를 응답한다.

 

7. 액세스 토큰이 만료된 경우

  1. [사용자] 로컬 스토리지 등에 토큰을 보관하고 데이터 요청(GET | /member)마다 액세스 토큰을 포함하여 전송한다.
  2. [서버] 요청과 함께 포함된 액세스 토큰이 유효한지 검증한다.
  3. [서버] 회원의 액세스 토큰이 만료된 경우 토큰 재발행 요청 혹은 접근을 거부하는 응답을 전송한다.
  4. [사용자] 리프레시 토큰과 함께 토큰 재발행 요청(POST | /auth/reissue)한다.
  5. [서버] 요청과 함께 포함된 리프레시 토큰이 유효한지 검증한다.
  6. [서버] 리프레시 토큰이 유효하다면 액세스 토큰과 리프레시 토큰을 재발급하여 응답한다.

 

4. JWT 탈취

1. 공격자가 페이로드 수정

 

사용자의 JWT 토큰이 공격자에게 탈취당하고, 공격자가 페이로드를 수정한 후에 서버로 요청을 보내면 보통 서버는 해당 토큰을 검증하고 접근을 거부한다. 이것이 가능한 이유는 JWT의 서명(Signature) 기능 때문이다. JWT는 일반적으로 페이로드를 JSON 형식으로 가지고 있다. 페이로드는 클라이언트와 서버 간에 정보를 교환하는 데 사용되고 서명되어 있어야 한다고 학습했다. 서명된 JWT 는 페이로드가 변경되었는지 여부를 확인할 수 있도록 해준다.

공격자가 탈취한 JWT 페이로드를 수정하고 사용하려고 하면 서버는 다음과 같은 과정을 거치게 된다:

  1. 서버는 JWT의 서명을 확인한다.
  2. 서명이 유효한 경우, 서버는 토큰이 변조되지 않았음을 알 수 있다.
  3. 그러나 페이로드가 수정되었기 때문에, 서버는 이 토큰을 거부하고 요청을 거부한다.

따라서 공격자가 JWT를 탈취하여 페이로드를 수정하더라도, 서버는 서명이 유효하지 않은 토큰으로 인식하여 요청을 거부한다. 이는 JWT의 무결성을 보장하는 중요한 보안 기능 중 하나이다.

 

정리하자면, 서버는 토큰 안에 들어있는 정보가 무엇인지 아는게 중요한 것이 아니라 해당 토큰이 유효한 토큰인지를 확인하는 것이 중요하기 때문에, 클라이언트로부터 받은 JWT 의 헤더, 페이로드를 서버의 Key 값을 이용해 서명을 다시 만들고 이를 비교하며 일치했을 경우 인증을 통과시킨다.

 

JWT 토큰은 Base64 로 암호화를 하기 때문에 디버거를 사용해서 인코딩된 JWT 를 1초 만에 복호화할 수 있다. 복호화 하면 사용자의 데이터가 포함된 페이로드가 노출된다. 그래서 페이로드에는 비밀번호와 같은 민감한 정보는 넣지 말아야 한다. 그럼 토큰 인증 방식 자체가 빛 좋은 개살구라고 생각할수도 있지만, 토큰의 진짜 목적은 정보 보호가 아닌, 위조 방지이다. 바로 위에서 소개했듯이, 서명에 사용된 비밀키가 노출되지 않는 이상, 데이터를 위조해도 서명에서 바로 걸러지기 때문이다.

 

 

2. 공격자가 JWT 토큰 그대로 사용

 

JWT 토큰의 페이로드를 수정하지 않고, 공격자 탈취한 토큰을 그대로 사용한다면 사용자의 권한과 특권을 얻을 수 있다. 이는 토큰 자체가 사용자를 대표하는 인증 수단이기 때문이다.

 

예를 들어, 만약 공격자가 JWT 토큰을 탈취하여 해당 토큰을 사용하여 API 요청을 보내면, 서버는 해당 토큰을 검증하고 토큰에 포함된 권한에 따라 요청을 처리한다. 공격자가 토큰을 가지고 있으므로 서버는 공격자를 해당 사용자로 인식하여 권한이나 특권을 부여할 수 있다. 이를 통해 공격자는 사용자의 권한으로 시스템에 접근하고 해당 권한을 악용할 수 있다.

따라서 토큰이 공격자에게 탈취된 경우, 해당 토큰이 유효한 동안 공격자는 사용자의 권한을 가지고 시스템에 액세스할 수 있게 된다. 이를 방지하기 위해서는 토큰을 안전하게 보호하고, 토큰의 유효 기간을 최소화하여 토큰의 탈취로부터 시스템을 보호해야 한다.

 

 

5. JWT 특징

 

JWT 장점

  1. JWT 헤더와 페이로드 그리고 비밀키를 갖고 서명을 생성하므로 데이터 위변조를 막을 수 있다.
  2. 인증 정보를 데이터베이스에 저장하지 않아도 된다.
  3. JWT 토큰 안에는 회원이 검증되었음을 증명하는 서명과 필요한 최소한의 정보를 갖고 있다.
  4. 회원 인증 정보를 저장하는 세션과 다르게, 서버는 무상태(Stateless)으로 서버 확장성이 우수하다.
  5. 쿠키와 다르게 토큰 기반은 다른 로그인 시스템 접근 및 권한 공유가 가능하다.
  6. OAuth 을 적용하여 서드파티 애플리케이션으로 소셜 로그인이 가능하다.
  7. 모바일 환경에 최적화된 인증 방식이다.

서버에서 가장 피해야 할 것은 데이터베이스 조회이다. 서버 자체가 죽는 경우도 있지만, 대부분 데이터베이스가 터져서 서버도 같이 죽는 경우가 허다하기 때문이다. 이런 점에서, JWT 토큰은 데이터베이스 조회를 안해도 되는 장점을 가지고 있다는 점이다. 만약 페이로드에 유저이름과 유저등급 을 같이 두고 보내면, 서버에서는 유저이름을 가지고 데이터베이스를 조회해서 유저 등급을 얻지않아도 바로 원하는 정보를 취할수 있다.

 

 

JWT 단점

  1. 토큰의 페이로드는 3종류의 클레임을 저장하기 때문에, 정보가 많아질수록 토큰의 길이가 늘어나 네트워크에 부하를 줄 수 있다.
  2. 페이로드 자체는 암호화 된 것이 아니라 BASE64 로 인코딩 된 것이기 때문에, 중간에 페이로드를 탈취하여 디코딩하면 데이터를 볼 수 있다. 따라서 페이로드에 중요 데이터를 넣지 않아야 한다.
  3. 무상태성(Stateless) 특징을 가지기 때문에, 토큰은 클라이언트 측에서 관리하고 저장한다. 따라서 토큰 자체를 탈취당하면 대처하기가 어렵게 된다.

 

2. Bearer

인증 스키마(Authentication Scheme)는 클라이언트가 서버로부터 보낸 인증 정보를 해석하는 방법을 나타낸다. HTTP 요청에서 인증 스키마는 Authorization 헤더에 포함된다. 일반적으로 BasicBearer가 자주 사용된다.

 

1. Basic Authentication

  • 사용자 이름과 비밀번호를 Base64로 인코딩한 문자열을 사용하여 인증한다. 보안상 취약하므로 HTTPS와 함께 사용하는 것이 좋다.

 

2. Bearer Authentication

  • JWT 와 같은 토큰 기반의 인증을 지원한다. 클라이언트는 토큰을 생성하여 서버에 전달하고, 서버는 검증하여 인증을 처리한다.

 

인증 스키마는 HTTP 헤더에 포함되며, 일반적으로 다음과 같은 형식을 가진다.

Authorization: <스키마> <인증 정보>

여기서 <스키마>Basic 또는 Bearer 와 같은 문자열이며, <인증 정보>는 해당 스키마에 따른 인증 정보이다.

 

 

1. Basic 인증

Basic 인증 방식은 말 그대로 가장 기본적인 인증 방식이다. 인증 정보로 아이디, 비밀번호를 사용한다. base64 으로 인코딩한 아이디:비밀번호 문자열을 Basic 과 함께 인증 헤더에 입력한다. 더 자세한 내용은 RFC 7617에 정의되어 있다.

 

 

RFC 7617: The 'Basic' HTTP Authentication Scheme

This document defines the "Basic" Hypertext Transfer Protocol (HTTP) authentication scheme, which transmits credentials as user-id/ password pairs, encoded using Base64.

datatracker.ietf.org

 

 

Base64 는 쉽게 복호화할 수 있다. 단순 base64 인코딩된 사용자 아이디와 비밀번호를 HTTP 로 전달하면 요청의 보안이 보장되지 않는다. Basic 인증을 사용하는 요청은 꼭 HTTPS, SSL/TLS로 통신해야 안전하다고 말할 수 있다.

Authorization: Basic base64({USERNAME}:{PASSWORD})

 

 

Basic 인증의 장점과 단점

 

Basic 인증 방식의 가장 큰 장점은 간단하다. 사용자 아이디와 비밀번호 외에 로그인 페이지나 별도의 인증 정보를 요구하지 않는다. 이러한 간편함 때문에 다수의 서비스가 Basic 인증 방식을 사용하고 있다. 쉬운 접근이 중요시되는 웹 서비스를 기반으로 만들어진만큼 Basic 인증은 단순하고 구축하기 쉽다.

 

그러나 Basic 인증 방식은 서버에 사용자 목록을 저장한다. 요청한 리소스가 많거나 사용자가 많으면 목록에서 권한을 확인하는 시간이 길어진다. 또한 서버에 현실적으로 저장할 수 있는 데이터는 한정되어 있어서 사용자가 많거나 사용자 변화가 잦은 서비스가 Basic 인증을 사용하면 서버에 부담이 매우 크다.

 

또한 Basic 인증 방식만으로는 사용자 권한을 정교하게 제어할 수 없다. 사용자가 꼭 필요한 리소스에만 권한을 주는 게 좋은데, Basic 인증 방식으로 세세하게 사용자의 권한을 설정하려면 추가 구현이 필요하다. 사용자 아이디, 비밀번호는 우리에게 가장 친근한 인증 방법이지만, 어떻게 보면 그만큼 구시대적이다. 복잡한 현대 IT 서비스는 더 정교한 인증 방식을 요구하기 때문에 Basic 인증 방식은 권장하지 않는다.

 

 

2. Bearer 인증

“Bearer”은 소유자라는 뜻인데, “이 토큰의 소유자에게 권한을 부여해줘” 라는 의미로 이름을 붙인 유래가 있다. Bearer 는 HTTP 헤더에 사용되는 인증 스키마 중 하나이다. 더 자세한 내용은 RFC 6750에 정의되어 있다. 

 

 

RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage

This specification describes how to use bearer tokens in HTTP requests to access OAuth 2.0 protected resources. Any party in possession of a bearer token (a "bearer") can use it to get access to the associated resources (without demonstrating possession of

datatracker.ietf.org

 

JWT를 사용하는 경우, 보통 토큰을 HTTP 요청의 Authorization 헤더에 담아서 서버에 전송한다. 이 때, Bearer 라는 인증 스키마를 사용하여 토큰이 JWT 토큰이라는 것을 나타낸다.

 

예를 들어, JWT 토큰이 "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" 라고 가정해보자. 이 토큰을 HTTP 요청의 Authorization 헤더에 담을 때는 다음과 같이 작성될 수 있다.

 

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

 

여기서 Bearer 는 인증 스키마를 나타내며, 그 뒤에는 JWT 토큰이다. 서버는 헤더의 Authorization 에서 Bearer 를 확인하여 해당 요청이 JWT 토큰을 사용한다는 것임을 인식하고, 해당 JWT 토큰을 검증하여 사용자 인증을 처리한다. R그리고 Bearer 인증은 차후에 학습하게 될 OAuth 와 상당히 밀접한 관계를 갖고 있다.