[Web] JWT



JWT (JSON Web Token)

JSON Format을 이용해서 Access Token을 다루는 표준

특징

  • Claim based Token

  • 두 개체에서 JSON 객체를 이용해 Self-contained 방식으로 정보를 안전한게 전달

  • 회원 인증, 정보 전달에 주로 사용

  • RFC-7519

Claim based Token ?
: 사용자에 대한 속성. 토큰 자체가 정보. key /value

Why Used?

기존 인증방식

1. 계정정보를 요청 헤더에 넣는 방식

-> HTTP 요청으로  Basic 인증방법

* Issue

절대 사용하면 안되는 방식. id, password 그대로 노출

서버에 신호가 갈 때 마다 계속 DB인증해야함

StateFul

클라이언트에게서 요청을 받을 때 마다, 클라이언트의 상태를 계속해서 유지하고,

이 정보를 서비스 제공에 이용

Flow

web-jwt-3

문제점
  1. Session의 한계 : 서버의 무리가 감

  2. Scale out의 문제 : 서버 확장이 어려움

  3. 플랫폼 다양화 : web, mobile 요청 처리 어려움

  4. CSRF의 문제 : 세션 보안 문제

  5. CORS의 문제 : 도메인 리소스 문제

  6. REST API : Stateless 지향

Stateless

상태를 유지 하지 않는다. 상태정보를 저장하지 않으면, 서버는 클라이언트측에서 들어오는 요청만으로만 작업을 처리.

정보와 회원 인증에 주로 사용하며 OAuth와 REST API가 대표적

플랫폼(Web, Mobile) 다양화

3. JWT

인증에 필요한 정보 암호화 시킨 AccessToken을 Http헤더에 실어서 보냄.

https://jwt.io/

https://github.com/jwtk/jjwt

https://www.jsonwebtoken.io/

구조

Access Token은 유의미한 정보를 가지고 있음

  • Header
  • Payload
  • Verify Signature

web-jwt-02

어떤 Type인지, 어떤 알고리즘이 들어있는지 들어감.

Payload

어떤 데이터가 담겨져 있는지. (userId, expire, scope 등) (Payload는 암호화되지 않기 때문에 외부에 노출하면안되는 데이터를 넣으면안됨.)

Payload에 넣는 데이터를 Claim이라하며 Claims라고함. (가능하면 Claims에 많은 정보를 담지않는게 좋음)

보통 id, name

Verify Signature

토큰이 유효한지 서명

(Base64 으로 인코딩한 Header, Payload + secret key를 포함하여 암호화) -> HMAC-SHA256

*서명을 만들 때 비밀키가 필요한데 보안에 주의해야함.

  • Header, Payload는 Base64 으로 인코딩될 뿐(16진수로 변경), 따로 암호화되지 않는다.
Flow

web-jwt-4

* Issue

  1. 유효기간

    이미 발급된 JWT에 대해서는 돌이킬 수 없다. 세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 됨.

    하지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능함. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 사용 가능함.

  2. PayLoad 정보가 제한적

    유저의 중요한 정보들은 Payload에 넣을 수 없다.

    (세션/쿠키 방식에서는 유저의 정보가 전부 서버의 저장소에 안전하게 보관)

  3. JWT의 길이입니다. 세션/쿠키 방식에 비해 JWT의 길이는 길다.

JWT 기본적으로 Claim에 대한 정보를 암호화 하지 않는다. 단순히 BASE64로 인코딩만 하기 때문에, 중간에 패킷을 가로채거나, 기타 방법으로 토큰을 취득했으면 토큰 내부 정보를 통해서 사용자 정보가 누출 될 수 있는 가능성이 있다.

특히 자바스크립트 기반의 웹 클라이언트의 경우 브라우져상의 디버거등을 통해서 토큰이 노출될 가능성이 높다.

그래서, 이를 보완하는 방법으로는 토큰 자체를 암호화 하는 방법이 있다. JSON을 암호화 하기 위한 스펙으로는 JWE(JSON Web Encryption)

Refresh Token

flow

web-jwt-5

보통은 Access Token에 대해 직접적으로 인증(direct authorization) 체크를 한다.

(Access Token이 서버 자원에 접근하려고 하면 서버가 토큰에 있는 정보를 읽어 스스로 인증여부를 결정)

반면에, Refresh Token은 Auth Server에 대한 체크가 필요 이때 다음과 같은 세 가지 사항을 고려

  1. 빠른 인증 과정을 위해 토큰에 정보를 적게 담아야 함.
  2. 노출된 Access Token에 대해서는 빠르게 만료시켜 리소스에 접근할 수 있는 가능성을 줄어야 합니다.
  3. Sliding sessions에 대한 처리가 필요.

Sliding session
: 정 기간 사용하지 않으면 만료되는 세션.

리프레시 관련해서 정확한 내용이 정해져 있는 것은 아니지만 일반적인 API를 보면 최초 발급시 Access Token과 Refresh Token 2개를 발급하고 Access Token으로 API를 사용하다가 만료시간이 지나면 만료시간을 길게 준 Refresh Token을 이용해서 Access Token을 다시 발급합니다.

{
	"code":401,
	"error":"invalid_token",
	"error_description":"The access token provided has expired."
}

토큰 재발급 로직

가장 일반적인 방법은 클라이언트가 토큰의 만료시간을 알 수 있기 때문에, 클라이언트에서 판단해서 만료시간이 넘었으면 토큰 재발급을 요청하는 방법.

좀 더 쉽게 구현하는 방법은 TokenExpiredError가 발생했을 때 재발급

OAuth? JWT?

JWT는 토큰 유형이고 OAuth는 토큰을 발급하고 인증하는 방법을 설명하는 일종의 프레임워크입니다. 기존의 /outh/token endpoint에 의해 발급되는 모든 토큰은 일종의 OAuth 프레임워크에 의해 관리

위의 토큰이 기존 OAuth에서 주로 사용하는 bearer 기반의 토큰 방식입니다. 다만 JWT는 토큰 자체에 유저 정보를 담아서 HTTP 헤더로 전달하기 때문에 유저 세션을 유지할 필요가 없고 가볍게 데이터를 주고받을 수 있다는 장점이 있습니다.

bearer 기반

결론

일반적인 웹 서비스처럼 cookie등을 이용해 세션 관리를 하는 방식을 사용할 수 없는 stateless한 REST API등은 토큰 방식의 보안을 이용 할 수 밖에 없습니다. stateless하기 때문에 매 요청에 대한 인증을 거쳐야 하는데, 이는 데이터베이스 등으로부터 토큰을 얻어오는 추가적인 I/O 작업이 불가피하고 이는 성능의 하락으로 이어집니다. 이를 해결해주기 위한 솔루션이 바로 JWT입니다.

JWT는 이런 장점과 함께 위에서 살펴봤듯이 여러가지 문제점들이 존재합니다. 토큰의 탈취에 대한 취약성, 서버의 클라이언트 제어 불가, 빈번한 로그인 요청 등의 문제에 대한 해결 방법은 RefreshToken이나 Sliding Sessions 등의 전략을 도입하는 것입니다. 하지만 이러한 전략들 역시 추가적인 IO 작업을 위한 성능 감소나, 편의를 위해 보안이 취약해지는 상황등이 발생할 가능성이 있습니다.

서비스가 결제가 필요한 보안에 민감한 컨텐츠를 다루고 있다면 비밀번호 한번 더 입력하는 것이 크게 문제가 되지 않습니다. 반면, 게시물에 글을 작성할때마다 비밀번호를 입력해야 한다면 사용자들은 매우 귀찮아 할 것입니다. 결국 모든 것을 얻을 수는 없습니다. 서비스마다 가진 고유한 특성을 고려해 보안 수준을 높일지, 사용자 편의성을 높일지를 결정해야 합니다.

Authentication (인증)

인가

.를 연결해서 이루어짐

실제로 JSON으로 나가면 안되기때문에 Base64 URL Encoding으로

표준 프로토콜 지원

SAML 2.0 (Security Assertion Markup Language)

XML 기반의 인증 프로토콜복잡하고 무거운 구조네이티브 어플리케이션, 모바일 환경에 적용하기에는 부적합

OAuth 2.0 (Open Authorization)

API를 사용하여 자원에 접근하기 위한 오픈 표준다양한 플랫폼 환경에서 권한 부여를 위한 프로토콜클라이언트에게 제한된 접근을 하도록 권한을 부여하는 일종의 프레임워크

OpenID Connect 1.0

OAuth 2.0 권한 부여 프로토콜을 기반으로 이를 확장한 프로토콜 JSON/REST 기반의 상호 운용 가능한 인증 프로토콜모바일 환경까지 고려한 설계로 다양한 플랫폼에 적용 가능

JWT, OAuth와 관계

토큰 기반의 인증 시스템을 공부 할 때 두가지의 개념이 헷갈릴 수 있다. JWT 는 Token 유형,

OAuth 는 Token을 발급하고 인증하는 방법을 설명하는 일종의 프레임워크

OAuth 2.0은 프로토콜을 정의합니다. 즉, 토큰 전송 방법을 지정하고 JWT는 토큰 형식을 정의합니다.
OAuth 2.0과 "JWT 인증"은 클라이언트가 리소스 서버에 토큰을 제공하는 2 단계 (단계)에서 비슷한 모양을 갖습니다. 토큰은 헤더로 전달됩니다.

그러나 "JWT 인증"은 표준이 아니며 how 를 지정하지 않습니다. 클라이언트는 첫 번째 (1 단계)에서 토큰을 얻습니다. 그것이 OAuth의 복잡성을 인식 한 곳입니다. 또한 클라이언트가 Authorization이라는 것을 통해 액세스 토큰을 obtain 할 수있는 다양한 방법을 정의합니다. 섬기는 사람.

따라서 실제 차이점은 JWT는 단지 토큰 형식이고 OAuth 2.0은 프로토콜입니다 ( may JWT를 토큰 형식으로 사용함).
첫째, 우리는 JWT와 OAuth를 구별해야합니다. 기본적으로 JWT는 토큰 형식입니다. OAuth는 JWT를 토큰으로 사용할 수있는 권한 부여 프로토콜입니다. OAuth는 서버 측 및 클라이언트 측 저장소를 사용합니다. 실제 로그 아웃을 원할 경우 OAuth2를 사용해야합니다. JWT 토큰을 사용한 인증은 실제로 로그 아웃 할 수 없습니다. 토큰을 추적하는 인증 서버가 없기 때문입니다. 제 3 자 클라이언트에 API를 제공하려면 OAuth2도 사용해야합니다. OAuth2는 매우 유연합니다. JWT 구현은 매우 쉽고 구현에 오래 걸리지 않습니다. 애플리케이션에 이러한 유연성이 필요한 경우 OAuth2를 사용해야합니다. 그러나이 유스 케이스 시나리오가 필요하지 않은 경우 OAuth2를 구현하는 것은 시간 낭비입니다.
JWT (JSON Web Tokens)- 이것은 단지 토큰 형식입니다. JWT 토큰은 발급자, 제목 (클레임), 만료 시간 등에 대한 정보를 포함하는 JSON 인코딩 된 데이터 구조입니다. 변조 및 신뢰성을 위해 서명되며 대칭 또는 비대칭 접근 방식을 사용하여 토큰 정보를 보호하기 위해 암호화 될 수 있습니다. JWT는 SAML 1.1/2.0보다 간단하며 모든 장치에서 지원되며 SWT (Simple Web Token)보다 강력합니다.

OAuth2 - OAuth2는 사용자가 브라우저 기반 웹 앱, 기본 모바일 앱 또는 데스크톱 앱과 같은 클라이언트 소프트웨어를 사용하여 데이터에 액세스하려고하는 문제를 해결합니다. OAuth2는 인증 용이며 클라이언트 소프트웨어는 액세스 토큰을 사용하여 최종 사용자를 대신하여 리소스에 액세스 할 수있는 권한을 부여받을 수 있습니다.

OpenID Connect _ - OpenID Connect는 OAuth2 위에 구축되고 인증을 추가합니다. OpenID Connect는 UserInfo Endpoint, ID Token, OpenID Connect 제공자의 동적 등록 및 세션 관리와 같은 OAuth2에 일부 제약 조건을 추가합니다. JWT는 토큰의 필수 형식입니다.

CSRF 보호 - 브라우저의 쿠키에 토큰을 저장하지 않으면 CSRF 보호 기능을 구현할 필요가 없습니다.
액세스 토큰을 갖는 요점은 무효화를 확인하지 않고 사용할 수 있다는 것입니다. 일부 데이터베이스가 유효하지 않은지 묻지 않아도 10000 개의 프런트 엔드 서버 사용자가 토큰으로 액세스 할 수 있습니다. 그러나 얼마 후 토큰이 만료됩니다. 사용자는 새로운 액세스 토큰이 필요하고 새로 고침 토큰을 보내고이 새로 고침 토큰은 일부 데이터베이스에서 확인됩니다. 만료 된 액세스 토큰을 확인하거나 상태를 확인할 필요는 없지만 남용을 토큰 수명으로 제한합니다.

데이터베이스에서 만료를 확인하지 않고 토큰을 수락 할 필요가없는 경우 서로 다른 두 개의 토큰이 필요하지 않습니다. 각 액세스마다 새로 고침 토큰을 사용할 수 있습니다.

워크 플로의 예는 다음과 같습니다.

사용자가 로그인하여 액세스하고 토큰을 새로 고칩니다. 액세스 토큰 수명 15 분, 새로 고침 토큰 5 일.

사용자는 액세스 토큰을 사용하여 서비스에 액세스합니다. 서비스는 서명과 수명 만 확인합니다. 데이터베이스 연결이 없습니다.

사용자가 로그 아웃하면 새로 고침 토큰이 데이터베이스 에서 만료 된 것으로 표시 됩니다.
사용자는 액세스 토큰을 사용하여 서비스에 액세스하지만 여전히 작동합니다
15 분 패스 액세스 토큰이 만료되면 사용자는 여전히 유효 기간 내에 새로 고침 토큰을 사용하여 새 액세스 토큰을 요청합니다. 서비스는 데이터베이스를 확인하고 토큰이 만료 된 것을 찾습니다. 사용자는 새로운 액세스 토큰을 얻을 수 없습니다.
전체 시스템은 세션을 무효화하는 데 걸리는 시간과 필요한 공유 / 동기화 된 데이터 소스에 대한 연결 양 사이의 균형입니다.

각 새로 고침마다 새로 고침 토큰을 교체 할 수 있지만 수명이 끝날 때까지 만료 된 모든 새로 고침 토큰 을 저장 해야합니다 .

보안 관점에서 새 토큰을 만드는 것이 합리적이지만 데이터베이스의 보안과 데이터 양 간의 균형을 유지합니다.

최악의 경우 (새로 고침 토큰의 수명이 없으면 절대 그렇게 하지 마십시오) 이제 각 사용자에 대해 하나의 토큰 대신 각 사용자에 대해 데이터베이스에 몇 분마다 하나의 토큰을 저장해야하며 다시 제거 할 수 없습니다.

참고 문서

https://bcho.tistory.com/999 https://velopert.com/2389 https://swalloow.github.io/implement-jwt http://www.opennaru.com/opennaru-blog/jwt-json-web-token/

https://velog.io/@city7310/%EB%B0%B1%EC%97%94%EB%93%9C%EA%B0%80-%EC%9D%B4%EC%A0%95%EB%8F%84%EB%8A%94-%ED%95%B4%EC%A4%98%EC%95%BC-%ED%95%A8-5.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D-%EA%B2%B0%EC%A0%95

https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

https://velog.io/@mycampground/Permissions-Token-Authentication-%EC%86%8C%EA%B0%9C

https://solidgeargroup.com/refresh-token-with-jwt-authentication-node-js/

https://swalloow.github.io/implement-jwt

https://velopert.com/2350

https://tansfil.tistory.com/58?category=255594

https://blog.ull.im/engineering/2019/02/07/jwt-strategy.html