CSRF를 웹 프레임워크 수준에서 방어하고, 프론트엔드 파트에 XSS를 방어하도록 하는 로직을 작성하도록 요청하는 것이 그 다음입니다.
JWT를 오해하면 이런 문제가 생깁니다!
용량이 훨씬 커진다
JWT는 기본적으로 크기가 큽니다. 커스텀 필드를 조금 넣었다 하면, 세션보다 훨씬 커집니다.
세션이 단순히 32bit의 문자열이라면, JWT는 못해도 그 10배 이상입니다.
로그아웃 기능이 사실상 “없다”
만료기간 전까지는 사실상 로그아웃 기능이 없다고 봐야합니다. 그리하여 이를 관리하기 위해 revocation list(일종의 만료 세션 리스트)를 따로 관리하면 될텐데, 그러면 기존의 세션 관리와 다를게 없지요. 게다가 용량이 훨씬 큰 토큰이 bandwidth를 잡아먹습니다.
그리하여 이런 해결책 또한 제기되었습니다[1]:
특정 로그아웃 콜을 받으면, 만료기간 전에 revoke 시킨 토큰의 글로벌 리스트를 갖고있습니다.
로그인 관련 서버가 매븐 해당 리스트에 대해 요청/응답 하지 않고 각 비즈니스 서버에 pub/sub 메커니즘을 통해서 즉각 처리하도록 합니다.
JWT 스펙 오용(abuse)으로 인한 보안 취약점
설정이 잘못되면 아무나 JWT 토큰을 생성할 수 있고, 유저가 다른 유저인 척 하는게 가능합니다.
JWT는 일회용 인증 토큰(single-use authorization token) 으로 쓰는 편이 좋겠습니다.
JWT 스펙을 살펴보면 다음과 같은 글귀가 나옵니다.
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.
(두 당사자 간에 전송되는 클레임을 나타내는 간결한 URL 안전 수단입니다.)
[…] enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
(디지털 서명하거나 MAC(메시지 인증 코드) 또는 암호화된 무결성 보호를 가능하게 합니다.)
다시말해 “claim”은 마치 명령처럼, 이것 할 수 있다던데 하는 일회성 식별표 정도로 인식하면 될 것 입니다. 그리고 암호화된 서명도 함께요.
그런 의미로 제가 참고한 모든 글들을 공통적으로 살펴보면, 세션과 JWT 토큰을 병행해서 쓰거나 각각 용도에 맞게 쓰는 것이 좋겠습니다.
제 생각에는 유저 간의 로그인은 세션으로 처리하고, 목표에 따라 잘 분리 된 API 서버간의 통신에서 JWT를 활용하는 것이 어떨까 싶습니다. 예를 들면 다음과 같겠습니다:
서버-서버 간 JWT를 담고 필요한 요청-응답을 수행해야 한다면 이는 충분히 말이되는 내용이다.
예를들어 SPA(Single-page Application) 에서 이것저것 많은 API를 호출해야 하고, 그 권한을 토큰을 통해 관리한다면 훨씬 효율적일 것으로 보입니다.
그 정도로 큰 서비스에 대해 다루게 될 때를 위해, 다음번엔 OAuth 2.0과 OIDC 및 Fedarated identity와 같은 키워드를 알아보는 글을 작성해보겠습니다.
마무리
이번 글을 통해, 아래 내용들을 살펴볼 수 있었습니다:
JWT를 세션 쿠키처럼 사용하면 아래와 같은 문제가 생깁니다.
좋은 줄 알았던 점은 사실 세션으로도 처리가 가능했습니다.
JWT를 잘못 사용함으로 인한 문제를 해결하기 위한 사이드 이펙트가 있었습니다.
쉽게 구할 수 있고, 장점만 보인다는 이유로 이해없이 기술을 사용하지 맙시다. 정말 위합니다. 보안과 기능의 장단점과 잠재적 문제사항을 모두 알고 있어야 합니다.‘보일러플레이트’ 및 템플릿에서 제외하고 기본 선택으로 만들지 마십시오.
기술을 잘못 쓰지 않도록, 많은 케이스를 배워둡시다. 필요와 적절한 요구사항에 따라 기술을 택해야겠습니다.