면접을 봤다...
면접관님께서 JWT와 Session에 차이 대한 질문을 하셨고
개념은 머리에 정리하고 있었으나 너무 큰 긴장으로 인해 답변을 제대로 하지 못했다
사실 툭치면 나와야 할 정도로 학습을 하지 않은게 문제일 수도 있기도 하고 면접에서 받은 질문에 대해 어떤 답변을 했으면 좋을지에 대해 한번 서치해보며 정리 해보고자 한다.
쿠키는 Key-Value 형식의 문자열의 모음이다.
클라이언트가 특정 사이트를 방문할 때 그 사이트의 서버를 통해 클라이언트의 브라우저에 저장되는 기록 정보 파일이다. 각 사용자마다의 브라우저에 정보를 저장하니 개인화, 고유 정보 식별이 가능하게 된다.
인증은 사용자의 신원을 확인 하는 것이며
어떠한 인증 요청(로그인, 이메일, 휴대폰 인증 등)을 통해 유효한 사용자인지 검증하는 과정이다.
인가는 인증된 사용자에게 특정 자원에 대한 접근 권한을 부여하는 과정이다.
나의 ClimbingWith(클라이밍 커뮤니티 서비스) 프로젝트에서
ADMIN, GYM_MANAGER, USER이 있다면
USER은 GYM_MANAGER의 권한인 암장 관리와 같은 접근할 수 없으며 GYM_MANAGER은 ADMIN의 권한인 GYM_MANAGER 지정과 같은 기능에 접근할 수 없다.
사용자가 접근할 수 있는 범위를 정하는 것 이라고 볼 수 있다.
세션은 민감한 정보가 클라이언트에 저장되는 쿠키의 보안적 이슈를 대비하기 위한 방식이다
세션은 민감한 정보는 브라우저가 아닌 DB, 혹은 메모리(Redis 등)에 저장하고 관리한다.
세션 방식은 인증 정보를 서버에 저장하기 때문에, 많은 사용자를 가지고 있는 서버일 경우 요청이 발생할 때마다 저장장치 접근이 발생하므로 서버에 부하가 발생할 가능성이 있다.
토큰(Token)은 사용자가 서버에 인증을 보내면 서버에는 인증 절차를 수행하고 유효한 사용자일 경우 인증이 완료되었다는 정보가 담긴 "토큰" 전달한다. 서버는 해당 토큰을 검증만 진행하며 서버는 DB를 조회하지 않고 누가 요청했는지 알 수 있다.
인증 정보가 서버에 저장되지 않고 클라이언트에 저장된다.
이것을 서버가 클라이언트의 상태를 저장하지 않는다고 하며
Stateless 하다고 하며 서버의 부담을 줄일 수 있으며
세션 사용이 불가능한 모바일에서도 사용이 가능하다
토큰 자체의 문자열의 길이가 길기 때문에 인증 요청이 많아질수록 네트워크 부하가 발생할 가능성이 있으며 Payload 자체는 암호회되지 않기 때문에 중요한 정보는 담아선 안된다.
또한 탈취 당했을 경우에 대처가 어렵기 때문에 결제와 같은 기능이 포함된 중요한 서비스에서는 사용에 유의해야한다.
말그대로 Web 인증에 필요한 정보들을 인코딩하여 Json 형태로 작성한 Token이다.
Base64로 인코딩된 "eyJ"로 시작하는 긴 문자열이다.

문자열에 .를 구분자로 HEADER, PAYLOAD, SIGNATURE로 구분된다.

alg: 서명 암호화 알고리즘을 선택한다.
typ: 토큰 유형을 명시한다.
서명 암호화 알고리즘의 종류는 크게 두가지로 볼 수 있다.
대칭키 알고리즘: 동일한 키로 SIGNATURE을 생성하고 검증을 함께 수행한다.
비대칭키 알고리즘보다 덜 복잡한 연산이기 때문에 연산 속도가 빠르다.
하지만 대칭키 전달 과정에서 탈취되면 누구나 유효한 SIGNATURE를 생성하고 검증에 통과될 수 있다.
비대칭키 알고리즘: 개인키로 SIGNATURE을 생성하고 공개키로 검증한다.
비대칭키는 2개의 키를 사용하며 더 복잡한 연산이 진행되므로 연산 속도라 드리다.
또한 2개의 키를 입력해야하기 때문에 토큰의 길이가 더 킬어지기도 한다
하지만 공개키 전달 과정에서 탈취당하더라도 개인키로만 암호화 복호화가 가능하므로 보안에 강력하다.
JWT에서 자주 쓰이는 대표적인 대칭키 알고리즘은 HMAC SHA256가 있으며
비대칭키 알고리즘은 RSA가 있다.
왜 이 두개가 가장 많이 쓰이는지는 이후 조사를 해보고
포스팅거리가 되는 내용이라면 한번 작성해볼예정이다.
토큰을 통해 사용될 정보들이 key-value 형태로 담겨 있다. 이 각각의 key를 Claim이라고 부른다.
HEADER를 포함해 PAYLOAD는 암호화되는 것이 아닌 BASE64를 통해 인코딩 되는 것이기 때문에 누구나 디코딩이 가능하므로 민감한 정보를 담아서는 안된다.
Claim은 3가지 종류가 있다.
필수X 사용이 권장되는 Claim들
sub: 토큰 제목
iat: 토큰 발행 시간
exp: 토큰 만료시간(NumericDate 형식)
iss: 발행자
jti: JWT ID(중복 방지용)
nbf: 토큰 활성화 날짜(해당 날짜 이후 활성화)
aud: 대상자
서로 다른 두 서비스 A, B가 인증 정보를 공유할 때 A, B에서 사용하는 JWT 클레임 이름이 같지만 그 의미나 사용방식이 다르면 문제가 발생할 여지가 있다.
누구나 사용할 수 있으며 이름 충돌을 방지로 URI 형식으로 기입한다.
즉 다른 서비스 등 수평확장을 고려할 때 사용하며 URI를 통해 클레임의 소유권과 의미를 명확히 한다.
특정한 서비스에서만 합의된 클레임이다.
"Private"라는 용어 때문에 햇갈릴 수 있는데
비공개 라는 의미가 아니므로
민감한 정보를 담아도 되는 곳이 아니다.
HEADER와 PAYLOAD를 서버가 저장중인 secret key를 합친 것을 HEADER에서 정의한 알고리즘을 이용하여 암호화 한다.
Signature은 secret key가 유출되면 사용자가 유효한 JWT를 무한히 생성할 수 있기 때문에 secret key 보관에 유의해야한다.
토큰을 사용할 때의 장점을 포함하며
토큰을 사용하는 다른 시스템에 접근 및 권한 공유가 가능하며
클라이언트가 인증정보를 갖기 때문에 분산 시스템, MSA 환경같이 수평확장에도 용이하다
많은 양의 데이터가 담기기 때문에 토큰 문자열의 길이가 길어져 네트워크 부하가 발생할 가능성이 있다.
PAYLOAD는 단순히 Base64로 인코딩 되어있기 때문에 민감한 정보는 담는 활용은 못한다.
클라이언트에 토큰을 저장하기 때문에 토큰 자체를 탈취당하면 대처가 어렵다.
JWT의 한계 중 토큰을 탈취당할 경우에 대한 해결법이다.
JWT는 서버에서 관리를 하지 않으므로 토큰을 탈취당할 경우 삭제가 불가능해지기 때문에 접근에 관여하는 토큰에 유효시간을 부여하는 식으로 해당 문제에 대응한다.
그러나 짧은 유효시간을 설정할 경우 다시 인증을 통해 새로운 Access Token을 발급받아야 하기 때문에 UX 관점에서 좋지 않다.
길게 할 경우 탈취당할 경우를 생각하면 보안에 문제가 생긴다.
그렇기에 이 문제를 해결하기 위한 것이 Refresh Token이다.
Refresh Token은 Access Token 만료시 Access Token을 재발급 해주는 역할을 한다.
즉 Access Token은 유효 시간을 짧게 설정하여 보안을 챙기고
Refresh Token은 유효시간을 길게ㅔ 설정하여 Access Token 재발급을 통해 UX를 챙긴다.
"JWT 토큰의 장점이 Stateless 하기 때문에 서버측 부담이 적다" 라고 말했고(더 잘 설명할 수 있었는데 긴장병 에휴 ㅠ)
면접관님이 이 외에도 사용하는 이유가 무엇이 있을까에 대해 여쭤보셨다.
답변은 못했지만 면접이 끝나고 하면 좋았을 법한 답변이 뭐가 있는지 하번 생각해보았다.
수평 확장(Scale out)에 유리하다
세션 방식은 서버간 세션 공유가 필요하기 때문에 Sticky Session(고정 세션 로드 밸런서가 특정 클라이언트에 첫 번째 요청을 처리한 서버를 기억하고 해당 클라이언트의 후속 요청을 처음 배정된 동일한 서버로 라우팅 하는 기술)이나 Redis 같은 별도 세션 스토어를 둬야하는데 JWT는 토큰 자체에 인증 정보가 있기 때문에 서버 인스턴스가 늘어나도 별도 동기화 작업이 필요 없어 MSA나 다중 인스턴스 환경에서 유리하다.
인증 서버와 리소스 서버의 분리(Decoupling)
JWT는 발급한 서버와 검증하는 서버가 분리될 수 있다. 예를 들어 인증을 담당하는 서버에서 발급한 Token을 여러 분산된 마이크로서비스에서 각자 서명만 검증해서 사용하다.
Self-contained - 정보 저장
JWT Payload에 권한 같은 claim을 담아서 매 요청마다 권한을 조회하는 DB 부담을 줄일 수 있다.
내가 진행한 클라이밍 커뮤니티 서비스 또한 Role 권한을 Claim에 담아서 매 요청마다 권한 DB 조회를 수행하지 않도록 분리했다.
표준 프로토콜 호환성
JWT는 RFC 7519 표준이고 OAuth2/OIDC 생태계와 잘 맞아. 소셜 로그인, SSO, 써드파티 연동할 때 표준 포맷이라 호환성이 좋다
도메인 간/플랫폼 간 사용 용이
쿠키 기반 세션은 도메인 제약(SameSite, CORS)이 까다로운데, JWT는 헤더에 담아 보내면 되니까 모바일 앱, SPA, 여러 도메인 간 인증에 비교적 자유롭다.
핵심적인 약점은 즉시 무효화(revocation)이 어렵다는 것이 있다 JWT는 만료 전까지 유효하기 때문에 아무리 Access Token의 유효시간이 짧더라도 해당 Access 토큰이 만료되기 전까지는 막을 방법이 없다. 라는 것 까지 기억해두면 좋을듯
오랜만에 면접이라 너무 긴장해버린것도 문제인듯