HTTP는 무상태(Stateless) 프로토콜을 사용합니다. 서버가 클라이언트의 상태를 보존하지 않으며 모든 요청이 독립적으로 다뤄집니다. 따라서 요청을 할 때마다 사용자가 누군지 서버에 알려주어야 합니다.
이를 위한 대표적인 방식이 바로 세션과 토큰입니다.
‘사용자가 웹 브라우저를 통해 웹 서버에 접속한 시점으로부터 웹 브라우저를 종료하여 연결을 끝내는 시점까지 같은 사용자로부터 오는 일련의 요청을 하나의 상태로 보고 그 상태를 일정하게 유지하는 기술’
즉, 서버와 클라이언트 간의 연결이 활성화 된 상태. 세션 ID를 사용해서 사용자의 로그인이 지속되는 상태.

실제 데이터는 서버 - 세션 저장소에서 관리되고, 쿠키로는 참조될 내용(세션 ID)만 실리기 때문에 보안면에서 상대적으로 안전합니다. 만약 세션 ID가 탈취당한 경우에라도 서버에서 해당 세션을 무효화 할 수 있습니다.
매 요청 시마다 서버가 세션 저장소에서 세션 ID를 조회하는 과정이 필요하고, 사용자가 많으면 많을 수록 서버에 대한 비용이 커진다는 단점을 가지고 있습니다. 또 Stateful하다는 특징 때문에 확장성이 떨어질 수 있습니다.
토큰은 Base64로 인코딩 또는 암호화된 3가지 데이터를 이어붙인 것으로 다음과 같은 구성을 가지고 있습니다.

{
"alg": "HS256",
"typ": JWT
}
토큰에 담긴 사용자 정보 등의 데이터를 클레임(Claim)이라고 합니다. 클레임은 JSON 형식으로 이루어져 있으며 등록된 클레임(Registered Claim), 공개 클레임(Public Claim), 비공개 클레임(Private Claim) 세 가지의 종류가 있습니다.
누가 누구에게 발급했는지, 이 토큰이 언제까지 유효한지 등 토큰 정보를 전달하기 위한 클레임으로, 등록된 클레임의 사용은 모두 선택적(Optional)으로 사용 가능합니다. 표준 스펙으로 이름이 정의된 7가지는 다음과 같습니다.
1. iss (Issuer) : 토큰 발급자
2. sub (Subject) : 토큰 제목
3. aud (Audience) : 토큰 대상자
4. exp (Expiration Time) : 토큰 만료 시간 - 시간은 NumericDate 형식으로 되어있어야 합니다.
5. nbf (Not Before) : 토큰 활성 날짜 - 해당 날짜 이전에는 토큰이 활성화 되지 않습니다.
6. iat (Issued At) : 토큰 발급 시간
7. jti (JWT Id) : JWT 토큰 식별자
사용자 정의 클레임, 충돌 방지를 위해 클레임 이름을 URI 형식으로 작성합니다. 공개되어도 안전한 정보들을 공개 클레임으로 정의합니다.
{
"http://example.com/is_root": true
}
사용자 정의 클레임, 클라이언트와 서버에서 협의된 클레임으로 미리 약속한 방식으로 사용해야 합니다. 비공개 클레임을 작성할 때 다른 클레임과 이름이 충돌하지 않도록 주의합니다.
{
"iss": "https://auth.example.com", // 등록된 클레임
"exp": 1677836800, // 등록된 클레임
"https://example.com/is_root": true, // 공개 클레임
"username": "geniee1220", // 비공개 클레임
"email": "geniee1220@example.com", // 비공개 클레임
}

Stateless하게 관리되기 때문에 서버의 비용이 줄어들고, 확장성이 좋다는 특징이 있습니다. 예를 들면, 세션을 서버측에 저장하고 여러 대의 서버를 사용하여 요청을 분산한 경우, 각 서버는 로그인 된 사용자의 세션 상태를 공유하지 않습니다. 따라서 로그인한 사용자는 처음 로그인한 서버에만 요청을 보내야합니다. 그러나 토큰을 사용하는 경우, 서버 간에 세션 정보를 공유할 필요가 없기 때문에 어떤 서버로 요청이 들어오더라도 문제가 발생하지 않습니다.
토큰을 서버에서 추적하는 게 아니기 때문에 탈취 당했을 때 무효화 할 수 있는 방법이 없습니다. 원론적으로 토큰 기반 방식에서는 세션 기반 방식처럼 강제 로그아웃 기능을 지원하지 않습니다.

로그인 시 수명이 몇 시간이나 몇 분 이하로 짧은 액세스 토큰(Access Token)과 비교적 길게, 대체로 2주 정도로 잡혀있는 리프레쉬 토큰(Refresh Token) 2개를 발급하는 방식. 토큰 기반 인증 방식의 단점을 보완하고자 나온 방법 중 하나입니다.
❗️액세스 토큰의 등록된 클레임에 유효기간이 명시되어 있기 때문에, 프론트엔드 단에서 4번 과정을 생략하고 바로 재발급 요청을 할 수 있습니다.
리프레쉬 토큰이 유효할 동안은 액세스 토큰이 만료될 때마다 다시 로그인 할 필요 없이 새로 발급을 받을 수 있습니다. 탈취가 된다고 하더라도 리프레쉬 토큰을 DB에서 삭제, 갱신이 되지 않게 하는 방식으로 강제 로그아웃을 시킬 수 있습니다. 단, 액세스 토큰이 살아있는 동안 바로 차단을 할 수 없다는 점이 한계점으로 꼽힙니다.
JWT는 변환(디코딩)이 매우 쉽기 때문에 비밀번호나 신용카드 번호 등 민감한 유저 정보들을 토큰에 포함하면 절대 안 됩니다.
Http Only Cookie 를 설정하면 XSS 공격을 방지할 수 있습니다.
❗️XSS(Cross Site Scripting)는 서버 측에서 제공되는 Script가 아닌 권한이 없는 사용자가 웹사이트에 Script를 삽입하여 의도치 않은 동작을 일으키는 공격을 말합니다.
리프레쉬 토큰 자체가 탈취될 가능성도 있기 때문에 Refresh Token Rotation 도 필요합니다.
None Algorithm Attack은 Header의 Algorithm을 None으로 변조하여 인증을 우회하는 공격입니다.
alg을 “none”으로 입력했을 때 서버에서 제대로 거부하고 있는지 확인해야 합니다.
서명 검증에 사용하는 Secret Key를 쉽고 간단한 값으로 지정하면 무작위 대입 공격에 뚫릴 수 있습니다.
생성용 키 private key / 검증용 키 public key 2개를 사용하거나 복잡도가 높은 키를 사용해야 합니다.
Reference
⓵ JWT란 무엇인가? 그리고 어떻게 사용하는가? (1) - 개념
https://velog.io/@vamos_eon/JWT란-무엇인가-그리고-어떻게-사용하는가-1
⓶ [Web] 쿠키(Cookie)와 세션(Session)의 차이, 쿠키란? 세션이란?
https://code-lab1.tistory.com/298
⓷ [Web] HTTP Only와 Secure Cookie 이해하기 https://nsinc.tistory.com/121
⓸ Access Token의 문제점과 Refresh Token https://hudi.blog/refresh-token/
⓹ JWT에서 Refresh Token은 왜 필요한가?
https://velog.io/@park2348190/JWT에서-Refresh-Token은-왜-필요한가
⓺ JWT(Json Web Token) 알아가기 https://brunch.co.kr/@jinyoungchoi95/1
⓻ 서버 인증 방식(세션/쿠키, 토큰) https://velog.io/@kingth/서버-인증-방식세션쿠키-토큰
⓼ 세션 VS. 토큰! JWT가 뭔가요? https://www.youtube.com/watch?v=1QiOXWEbqYQ
⓽ 우아한 테크 [10분 테코톡] 🎡토니의 인증과 인가 https://youtu.be/y0xMXlOAfss
⓾ JWT(Json Web Token)란 무엇인가!! https://velog.io/@ye-ji/JWTJson-Web-Token에서-정보빼기