JWT (JSON Web Token) 란 무엇인가?
- open standard 로 RFC 7519 에 표준에 관한 내용이 기재되어 있다.
- 통신에 JSON 을 이용하여 JSON 객체를 통해 두 당사자 간 정보를 보안이 적용된 안전한 방식으로 전달한다.
- JWT 는 다른 인증방식과 비교했을 때 컴팩트하며 스스로 정보를 담고 있다.
- 디지털 서명이 되어있기 때문에 정보에 인증이 되어 있고 믿을 수 있다.
- 보통 HMAC 알고리즘과 함께 secret 을 이용하여 서명한다.
- RSA, ECDSA 를 이용한 public/private key 쌍을 이용할 수도 있다.
아래 설명에
claim
이란 용어가 나오는데, 이는 JWT 를 이용해 전송되는 암호화된 정보를 말한다. JSON 오브젝트에서 다루는 프로퍼티의 이름이기도 하다. ex){ "sub": "12345" }
에서sub
는subject claim
이다. 전송되는 JSON 오브젝트 전체를payload
라고 하며,claim
은 그 속에 있는property
이다. 더 정확하고 자세히 알고 싶다면 공식문서를 참고해보자.
JWT 는 두 당사자간 보안 정보를 주고받기 위해 암호화될 수도 있음에도 불구하고 우리는 인증된 토큰들에 집중할 것이다. 인증된 토큰은 토큰 내부에 들어있는 claim
을 숨기면서도 claim
이 온전한지 검증할 수 있다. 즉, 암호화되어 실제 내용이 무엇인지를 몰라도 이 토큰이 온전한지 자체는 알 수 있다는 것이다.
ex) TWFuIGlzIGRpc3Rpbm.kgYSBwZXJzZXZlc.m5hbCBwbGVhc3VyZS4=
라는 암호화된 토큰 값이 왔을 때, 이 토큰이 무슨 내용인지는 몰라도 이 토큰이 온전한지는 알 수 있다는 것이다. 일단 온전함을 확인한 후에 secret
을 통해서 평문으로 복호화 되면 무슨 값인지도 알 수 있게 될 것이다.
토큰이 public/private 키 쌍을 이용해 인증될 때는 서명이 암호화할 때 가지고 있던 private key 를 이용해 이루어졌다는 것을 인증할 수 있다.
JWT (JSON Web Token) 는 언제 쓸까?
Authorization (인가)
가장 일반적인 시나리오이다. 일단 유저가 로그인하면 이후의 요청은 JWT 를 포함하여 사용자가 특정 경로, 서비스, 리소스에 접근하는 것을 허락해준다.
아주 작은 오버헤드와 다른 도메인간에서도 쉽게 이용될 수 있다는 이유 때문에, Single Sign On 은 요즘 JWT 를 널리 사용하는 기능 중 하나이다.
Information Exchange (정보 교환)
이를테면 public/private key 쌍을 이용하여 JWT 가 서명될 수 있기에, JWT 는 안전하게 정보를 주고받는데도 잘 쓰인다.
public/private key 쌍을 이용하면, 보낸 사람에 대한 인증도 되며 서명은 헤더와 페이로드를 사용해 계산하므로 내용이 변조되지 않았다는 사실도 알 수 있다.
JWT 의 구조는 어떻게 되는가
.
으로 구분되어 3가지 영역을 갖는다.
- 헤더 (Header)
- 내용 (Payload)
- 서명 (Signature)
header.payload.signature
와 같은 형태이다. 물론 Base 64 URL 로 인코딩된 값이 들어와서 실제로는 TWFuIGlzIGRpc3Rpbm.kgYSBwZXJzZXZlc.m5hbCBwbGVhc3VyZS4=
와 같은 형태로 보일 것이다.
헤더 영역
헤더는 일반적으로 2가지 부분이 있다.
- 토큰의 타입이 무엇이냐 (보통은
JWT
이다.) - 어떤 알고리즘을 썼냐
{
"alg": "HS256",
"typ": "JWT"
}
위와 같이 작성된 이후에, Base 64 URL 로 인코딩 된 값이 될 것이다.
내용 (payload) 영역
내용은 claim
이라 불리는 것을 포함한다. claim
은 엔티티와 추가적인 데이터에 대한 명세서이다. 3가지 종류의 claim 이 존재한다. registered, public, private claim
이 존재한다.
Registered claims
: 상호정보교환이 가능하고, 유용한claim
을 제공하기 위해 필수적이진 않지만 권장되는 미리 정의된claim
의 집합이 있다.iss
: issuerexp
: expiration timesub
: subjectaud
: audience- 기타 등등... 공식문서 참조
JWT 는 컴팩트해야 하기 때문에 3글자로 제한한다.
Public claims
: JWT를 사용하는 사용자가 자유롭게 정의할 수 있다. 그러나 충돌 방지를 위해 이에 대해 IANA JSON Web Token Registry 에 정의되어 있다. 혹은 충돌 방지 네임스페이스를 포함한 URI 로 정의되어도 된다.Private claims
:public
혹은registered
가 아닌private
을 사용하기로 합의한 당사자간 정보를 교환하기 위한 사용자 정의claim
이다.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
위와 같이 작성된 이후에, Base 64 URL 로 인코딩 된 값이 될 것이다
서명 (signature) 영역
서명 영역을 생성하기 위해서는 인코딩된 헤더, 인코딩된 내용(payload), 시크릿, 헤더에 명시된 알고리즘이 있어야 한다.
HMAC SHA256 알고리즘을 사용하고 싶다면, 서명 영역은 아래와 같이 만들어진다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
서명은 메세지가 변조되지 않았음을 확인한다. 그리고 만일 private 키로 서명된 경우엔, JWT 의 송신자가 데이터를 보내기로 한 그 당사자인지 확인할 수도 있다.
모두 다 합쳐보자
HTML 과 HTTP 환경에서 정말 쉽게 3개의 .
으로 구분된 Base64-URL 문자열을 넘겨줄 수 있다. XML 을 기준으로 한 표준 SAML 과 비교했을 때 좀 더 컴팩트하다.
JWT 를 가지고 놀고 싶다면, jwt.io Debugger에서 디코딩도 해보고 확인도 해보고 JWT 를 만들어보기도 할 수 있다.
JWT 웹 토큰은 어떻게 동작하는가?
인증에서는 보통 사용자가 로그인하면 JWT 가 발급되고, 이후 사용자는 매 요청시 JWT 를 들고다니며 인증된 상태를 유지한다. JWT 는 인증정보가 되기 때문에 보안상의 이슈를 잘 다뤄주어야 한다. 일반적으로, 토큰을 필요 이상으로 오래 들고있지 않는 것이 좋다.
브라우저 저장소에는 충분한 보안 처리가 되어있지 않으므로 민감한 세션 데이터를 넣는 것이 권장되지 않는다.
사용자가 보호된 경로나 리소스에 접근하길 원할 때마다, 사용자 에이전트는 JWT를 보내야 한다. 일반적으로 Authorization
헤더에 Bearer
스키마를 이용한다. 헤더는 아래와 같이 구성될 것이다.
Authorization: Bearer <token>
이는 몇몇 경우에서는 무상태 인가 매커니즘이 될 것이다. 서버의 보호되는 라우팅 경로는 Authorization
헤더에 있는 JWT 가 유효한지 검사할 것이고 유효하다면, 사용자는 해당 리소스나 경로에 접근할 권한을 얻게 된다. 항상 그렇진 않겠지만, 만일 JWT 가 필수적인 데이터를 포함한다면, 특정한 작업을 위해 데이터베이스 쿼리를 날려야 하는 필요가 줄어들 것이다.
HTTP 헤더를 통해 JWT 를 전달한다면, HTTP 헤더가 너무 커지지 않도록 주의해야 한다. 몇몇 서버는 8KB 가 넘어가는 헤더는 받아들이지 않는다. 모든 유저의 권한처럼 JWT 내부에 너무 많은 정보를 넣으려 한다면, Auth0 Fine-Grained Authorization 과 같은 대체 솔루션이 필요해질 것이다.
Authorization
헤더에 토큰이 담겨서 보내진다면, 쿠키를 사용하지 않으므로, Cross-Origin Resource Sharing (CORS) 는 이슈가 되지 않을 것이다.
아래의 다이어그램은 JWT가 어떻게 얻어지는지 그리고 API 혹은 리소스를 접근하기 위해서 어떻게 사용되는지를 보여준다.
- 인가 서버에 인가 권한을 요청한다.
- 인가가 허가된다면, 서버에서는 액세스 토큰을 발급한다.
- 애플리케이션은 발급된 액세스 토큰을 통해 API 서버 혹은 리소스에 접근한다.
비록 토큰 내부에 있는 정보를 변조할 수는 없지만, 토큰 내부에 있는 정보는 사용자나 다른 당사자들에게 유출될 수 있다. 토큰 내부에 시크릿에 대한 정보는 포함하지 않는 것이 좋다.
우리는 왜 JWT 를 써야 하는가?
JWT
의 비교군으로 SWT(Simple Web Token) 과 SAML(Security Assertion Markup Language Token) 이 있다.
컴팩트하다
JSON 은 XML 보다 간결하므로, 인코딩 되었을 때의 크기도 훨씬 작다. 이로 인해 HTML 이나 HTTP 를 이용해 정보를 넘기기에 좋다.
보안성이 좋다
SWT 는 HMAC 알고리즘과 공유되는 시크릿을 통해 대칭으로 서명될 수 있다. 하지만 JWT 와 SAML 은 public/private 키 쌍을 이용할 수 있다. (X.509 certificate 의 형태로 서명이 가능하다.) 그러나 XML 은 JSON 에 비해 보안상의 구멍 없이 서명을 진행하기 매우 까다롭다.
호환성이 좋다
대부분의 프로그래밍 언어에서 JSON 파서는 지원하고, 객체와 매핑도 쉽게 할 수 있도록 되어있다. 그러나 XML 의 경우에는 덜 대중적이어서 그렇지 않은 언어들이 있을 수 있다.
또한, JSON 형태는 인터넷 스케일에서 기기에 상관없이 어디서든 쓰인다. 특히 모바일과 같은 클라이언트에서 쓰기도 매우 좋다.
JWT 가 압도적으로 간결하다.
레퍼런스
'보안' 카테고리의 다른 글
스프링 시큐리티에서 Authority 와 Role 의 차이는? (2) | 2023.12.05 |
---|---|
리프레시 토큰 (refresh token) 을 사용하는 이유를 알아보자 (0) | 2022.10.13 |
Method Security of 스프링 시큐리티 (0) | 2022.05.07 |
CSRF 공격 정리 (feat. 스프링 시큐리티) (0) | 2022.05.03 |
인증 (Authentication) 과 인가 (Authorization) 의 차이를 알아보자. (0) | 2022.05.02 |