티스토리는 왜 마크다운 문법이 잘 안먹을까...
원본은 아래 링크에 있음
https://iodized-pond-201.notion.site/JWT-e273e7ea07f743da856ceea1adfc39fd?pvs=4
클릭!
JWT
Json Web Token
등장 배경
기존 세션 기반 인증 방식
stateful (클라이언트, 서버의 state를 (서버에) 저장)
- 실제 인증 정보는 서버(세션)에 저장하고, 클라이언트에게 세션의 id만 전달하는 방식
- 쿠키로 전달, 해당 session-id에 해당하는 인증정보가 존재하면 인증된 사용자로 판단함.
- 실제 인증 정보는 서버(세션)에 저장하고, 클라이언트에게 세션의 id만 전달하는 방식
단점
세션이 많아질수록 매 요청시 해당 세션을 찾아야하므로 서버 부담이 증가함.
다중 서버 환경에서 모든 서버가 동일한 세션을 유지하기 위해 별도의 조치 필요
유연한 확장이 가능한 scale out에 불리함
sticky session
클라이언트 요청이 특정 서버의 세션에 sticky하게 연동되는것.
트래픽 비효율적인 분산 위험성
서버 장애시 해당 서버의 세션 모두 유실
세션 클러스터링
- 특정 서버에서 세션에 생성되면 복제
- 비효율적인 메모리 관리
- 세션 생성시 모든 서버에 전파하는 과정에 많은 트래픽 발생
- 복제 과정의 시간차로 세션 불일치 이슈 위험성
세션 스토리지 분리
- 세션만 외부로 분리
- 메모리 관리, 세션불일치 문제등 해결됨.
- 하지만 세션스토리지가 하나뿐이라 장애 발생새 모든 세션 데이터 유실
- 세션 스토리지를 여러개로 구성해서 해결한다고 함
(참고) Scale up & Scale out (다중 서버 환경이 유리한 이유)
스케일업 : 단일 서버의 스팩 업그레이드
- 스팩 변경시 시스템을 이전하는 시간이 필요함
- 단일 장애점을 가지므로, 문제 발생시 치명적임
- 하지만, 항상 일관된 데이터 보장됨
스케일 아웃 : 여러개의 서버를 추가로 설치하는 방식
여러개의 서버를 하나처럼 사용하기 위해 클러스터링 작업에 비용이 발생
하지만 장애 발생시 다른 서버로 대체 가능
무한하게 확장 가능
서버 중단 없이 유연하게 서버의 숫자를 조절할 수 있음.
SAML 2.0
- JWT 이전에도 JWT와 같이 모든 인증 정보를 토큰에 담고있는 방식이 있었음 (클레임 기반)
- Assertion 이라는 개념으로 XML 안에 이러한 Claim 정보를 넣어서 넘길 수 있었음.
- 그러나 전체적인 사이즈가 너무 크고, 구조가 복잡하여 쉽게 접근이 어려움 + 파싱에 오버헤드가 큼
- 표준화가 잘 되어있어 SSO(한번의 로그인으로 여러 서비스 엑세스)에 많이 사용됨(상호 운영성이 좋음)
- 개방된 인터넷에서 사용하도록 개발되었지만, 실제로는 기업 네트워크 내에서 SSO에 가장 많이 사용함.
- 마이크로스프트는 인증에는 SAML을 사용하고, 권한 부여는 OAuth (JWT)를 사용하는 식으로 조합해서 사용하는 경우도 있음.
- 추가 조사 필요
- 위 사진은 토큰 길이의 차이 (출처 : JWT Docs)
JWT 방식
- stateless (클라이언트,서버의 state를 저장하지 않음)
- 토큰 내부에 인증에 필요한 정보들을 넣어둠 (클레임(사용자에 대해 작성된 정보) 기반)
- 서버간 세션 불일치 문제, 서버 부하 증가와 같은 문제를 해결함
- 단점
- Session기반의 경우 Session id만 주고받지만, 이건 정보를 주고받으므로 네트워크 자원을 상대적으로 많이 소모 (But SAML보다는 확연히 작음.)
- 발급된 이후로 서버는 토큰에 대한 제어권을 완전히 상실함.
- HTTPS 통신으로만 통신함
- accessToken, refreshToken 방식으로 어느정도 보완이 가능함.
- Access Token은 유효기한을 짧게 설정해서 위험성을 줄임.
- Refresh Token은 유효기간을 길게 설정하는 대신, 추가적인 검증을 필요로하게 하며, 탈취 위험을 줄이기 위해 토큰 갱신시를 제외하고는 사용하지 않음.
- Refresh Token의 추가적인 검증
- 카카오
- client_id를 필수로 함께 요청
- (선택) client_secret 를 함께 요청 ([내 애플리케이션] > [보안]에서 설정 가능)
- 네이버
- client_id, client_secret를 필수로 함께 요청
- 카카오
- Refresh Token의 추가적인 검증
JWT 구성
헤더 . 페이로드 . 서명
이렇게 점으로 구분되어 있음.
Header 헤더
{ "alg" : "HS256", "typ" : "JWT" }
서명 생성 알고리즘과 토큰의 타입 지정
일반적으로 사용하는 alg 타입
none → 사용하면 안됨. 누구나 생성 가능하여 취약함 (대부분 라이브러리에서 사용 X)
HS256
- 대칭키 알고리즘.
- 비밀키 한개로 암호화/복호화 모두 수행
- 성능이 우수하나 비밀키 하나가 노출되면 모든 보안이 뚫림.
RS256
비대칭키 알고리즘.
공개키로 암호화 - 비밀키로만 복호화 가능
공개키는 노출되어도 복호화가 불가능하므로 ‘상대적으로’ 보안에 유리
암호화/복호화를 서버에서만 수행하는 경우 trade-off 고려
→비밀키를 사용하는 사람(서버)가 한군데면 HS256과 보안적 차이 거의 없음
복호화에 많은 자원이 소모됨
Payload 페이로드
암호화되지 않으므로 중요한 개인정보 담으면 안됨!
Registered Claim 표준(등록) 클레임
아래는 JWT 사양이 정의된 표준 필드이고, 모두 선택적이다.
{ ///표준 필드 "iss" : "issure 토큰 발급자" , "nbf" : "not before 토큰 활성 날짜/시간 (NumericDate)", "exp" : "expiration time 만료시간 (NumericDate)", "iat" : "issued at 토큰의 발행된 시간 (NumericDate)", "sub" : "subject 토큰의 제목", "aud" : "audience 토큰을 사용할 수신자", "jti" : "JWT Token Id 토큰 식별자", }
- NumericDate?
- Unix Time (그리니치 표준시 기준 1970년 1월 1일 0시 0분 0 초) 으로부터 지난 ‘초’ 를 숫자로 나타낸 시간 단위.
- NumericDate?
Public Claim 퍼블릭 클레임
- JWT를 사용하는 사람들에 의해 정의된 클레임.
- 중복 방지를 위해 IANA (링크) 에 직접 등록해서 사용하거나, URI형태로 중복을 방지해야함.
{ "email": "sample@domain.com", //등록된 public claim "profile": "http://domain.com/image.png", // 등록된 public claim "http://domain.com/xxx/yyy/is_admin": true // URI형태의 claim }
Private Claim 프라이빗 클레임
- 서버-클라이언트 사이에서만 협의된 클레임
- 위 Public Claim, Registered Claim과 중복을 피해서 만들면 된다.
Signature 서명
검증 방식
- access token 사용
- 사용자 로그인 정보 전송
- 사용자 로그인 정보가 맞는지 검증하고,
- 맞으면 비밀키로 암호화된 JWT발급
- 응답으로 AccessToken 보내줌(필요시 + RefreshToken)
- JWT를 헤더에 넣고 요청을 보내면
- 서버는 헤더,페이로드를 암호화한 해시값이 서명과 일치하는지 여부 확인
- 검증 완료되면 요청한 데이터 혹은 거절 오류를 보냄
- Refresh Token 발급
- 아래 과정은 사용자한테 별도의 알림이 없이 진행됨(silent refresh)
- 위 과정의 5번 과정을 시도(JWT에 access token 넣고 보냄)
- access token이 만료되었다는 오류 메세지가 돌아옴
- refresh token으로 새로운 토큰 발급 요청→재발급
- 위 과정의 5번 과정 다시 시도(access token으로 데이터 요청)
JWT 주의사항
JWT토큰은 발급 이후 서버의 손을 떠나기 때문에, 그 토큰을 안전하게 관라하는것이 핵심
Access Token, Refresh Token 이원화하여 관리
토큰의 만료시간 설정
- AccessToken은 매번 사용하여 유출될 위험이 높은것으로 간주, 유효기간을 짧게 설정
- RefreshToken은 추가적인 보안 대책(클라이언트정보등), 사용 용도를 제한하여 보안을 강화한뒤, 유효기간을 길게 설정.
JWT 토큰의 저장 위치 고려
쿠키
Persistant Cookie, Session Cookie가 있음
- 간단하게 말하면, 세션쿠키는 세션스토리지처럼 창 닫으면 끝.
쿠키는 로컬스토리지와 다르게 모든 사이트의 쿠키가 한곳의 공간에 저장됨.
쿠키는 아래 옵션이 충족되면 자동으로 전송됨
보안옵션
HTTP Only
- 자바스크립트로 접근 차단
- 악의적인 자바스크립트 코드 공격(XSS) 방지
- 자바스크립트로 접근 차단
secure : HTTPS을 사용하지 않으면 전송되지 않음
SameSite : 쿠키에 사용 범위를 제한 (사칭 사이트 이용한 탈취 (CSRF)방지)
strict : 쿠키에 명시된 도메인과 일치하는 경우에만 전송
lax : 링크를 클릭해서 이동하는 경우등 일부의 경우에 한해 타사 도메인에서 전송 허용
예시 : 사내 시스템에 로그인 후 강의신청을 클릭하면 a태그를 통해 외부 강의 사이트로 랜딩됨. 로그인 관련 값은 쿠키를 사용해 공유됨.
- strict인 경우 자사 도메인 쿠키가 아니므로 접근 불가
- lax의 경우 사내 시스템 로그인 정보에 접근이 가능함.
none : 적용 안함 (이럴경우 secure 옵션 필수)
스코프
- 쿠키는 자동으로 붙어서 전송되기 때문에, 민감한 정보는 스코프를 최대한 줄여서 최소한으로 전송하도록 해야함.
- Domain
- Domain=example.org 라고 하면 api.example.org도 포함됨.
- Path
- Path=/refresh-token 으로 명시하면 해당 경로에서만 전송됨.
웹 스토리지
- 로컬스토리지/세션스토리지가 있으며, 같은 객체를 상속하므로 매서드 동일함.
- 로컬스토리지
- 동일 브라우저, 동일 컴퓨터에서 영구적으로 남아있음
- 로그인 정보(토큰 등)
- 동일 브라우저, 동일 컴퓨터에서 영구적으로 남아있음
- 세션스토리지
- 데이터가 오리진 + 탭에 종속됨
- 일회성 로그인정보, 입력폼 저장등
- 데이터가 오리진 + 탭에 종속됨
- 장점
- 오리진에 데이터가 종속됨. (피싱 사이트 CSRF 공격에서 안전)
- 자바스크립트로 편리하게 접근 가능..
- 쿠키보다 큰 용량 저장 가능함(2.5~10MB, 텍스트로 MB단위면 어마어마한 양임)\
- 쿠키와 달리 자동전송의 위험이 없어 중간이 탈취될 위험이 상대적으로 적음
- 단점 : 자바스크립트로 access가 가능하여 악의적 스크립트 공격(XSS)에 취약함.
로컬변수 (Zustand등등)
- 새로고침 하면 날아가버림
- 해당 자바스크립트 코드에서만 유효하므로 해당 페이지(SPA인 경우 해당 사이트)에서만 사용 가능.
- 악의작인 스크립트가 주입된 사이트(XSS)공격에 완벽히 대응 안됨
Indexed DB
사실 이건 토큰 저장에는 적합하지 않으나, 참고용으로 기록
- 용도
- 웹 스토리지로 용량이 부족할 경우 사용
- 음악서비스 FLO에서 도입하였다고 함 링크
- 장점
- HDD의 50%정도로 용량이 매우 큼.
- 로컬 스토리지가 string타입으로만 사용 가능하지만, 이건 key-value형태의 객체로 관리 가능.
- string으로 별도로 컨버팅하는 로직 불필요
- 단점
- 로컬스토리지의 모든 단점 + 사용방법 다소 복잡함.
- 용도