안녕하세요 제이븐입니다 😁
저번 포스팅에 이어서 이번에는 JWT가 무엇이고 왜 JWT를 사용하는지에 대해서 알아보겠습니다.
서버가 클라이언트 인증을 확인하는 방식에는 대표적으로 쿠키, 세션 그리고 이번에 알아볼 토큰 3가지 방식이 있습니다.
Cookie 인증 방식은 웹 애플리케이션에서 사용자가 로그인한 후, 서버가 사용자의 인증 상태를 유지하기 위한 방법 중 하나입니다.
Cookie는 사용자의 웹 브라우저에 저장되는 작은 데이터 조각입니다. 이 데이터는 Key-Value 형식으로 저장합니다.
Cookie의 특징은 도메인에 제약을 받기 때문에 같은 브라우저에서 접근 하더라도 도메인이 다르면 쿠키를 읽을 수 없습니다. 즉, 기본적으로 쿠키를 설정한 도메인에서만 유효하며, 다른 도메인에서 접근할 수 없습니다. (보안에는 취약하지만 격리 되어있습니다.)
Cookie는 일반적으로 서버가 클라이언트에게 응답할때 Set-Cookie 를 통해서 필요한 쿠키 정보를 보냅니다.
그리고 브라우저에서는 Set-Cookie 에 명시 되어있는 쿠키를 저장하고 이후 서버와 통신에서 자동으로 쿠키를 포함시켜서 보내게 됩니다.

1. 클라이언트가 서버에 접속 요청을 보냅니다.
2. 서버는 클라이언트에 응답을 보낼때 클라이언트에 저장하고 싶은 정보를 응답 헤더의 Set-Cookie 담습니다.
3. 이후 클라이언트의 요청에 매번 Cookie를 담아서 보냅니다.
쿠키의 보안적 이슈 때문에, 세션은 민감한 인증 정보를 브라우저가 아닌 서버측에 저장하고 관리하게 되었습니다.
세션 객체는 (Key: Session Id, Value: 구성요소)로 구성되어있습니다.
세션 인증은 웹 애플리케이션에서 사용자의 로그인 상태를 관리하고, 서버가 사용자에 대한 상태 정보를 유지하기 위해서 생성하는 임시적인 데이터 저장소입니다.
세션 인증은 쿠키와 함께 작동하게 되는데 서버가 세션을 생성하고 Session Id를 클라이언트에 넘겨주며 클라이언트는 이 Session Id를 포함해서 서버에 요청을 보내고 서버는 Session Id와 매칭해서 사용자를 인증합니다.
세션도 쿠키와 마찬가지로 도메인에 제약을 받습니다.
추가적으로 세션은 유지 시간이 존재하기 때문에 일정 시간 동안만 유효합니다. 세션이 만료되면 삭제 되고 다시 로그인하여 세션을 생성해주어야 됩니다.
유저가 로그인을 하면 서버에서 세션(Key: Session Id, Value: 구성요소)을 생성하고 Key에 해당하는 Session Id를 클라이언트에 저장(Set-Cookie)합니다.
이후 클라이언트는 저장된 쿠키(Session Id)로 서버에 요청을 보내고 서버는 받은 Session Id로 검증하고 응답하는 방식입니다.

클라이언트가 서버에 접속을 하면 서버에서 해당 클라이언트에게 인증되었다는 의미로 '토큰'을 부여합니다.
이 토큰은 유일하며 토큰을 발급받은 클라이언트는 또 다시 서버에 요청을 보낼 때 요청 헤더에 토큰을 심어서 보냅니다.
그러면 서버에서는 클라이언트로부터 받은 토큰을 서버에서 제공한 토큰과의 일치 여부를 체크하여 인증 과정을 처리하게 됩니다.
토큰은 앱과 서버가 통신 및 인증을 할때 가장 많이 사용되는 방법입니다.
앱에서는 브라우저 처럼 쿠키나 세션을 관리해주는 기능이 따로 없기 때문입니다.

JWT는 Json Web Token의 약자로 JSON 객체를 사용하여 정보를 안전하게 전송하기 위한 웹 토큰중 하나 입니다.
유저를 인증하고 식별하기 위한 토큰 기반 인증입니다.
JWT는 JSON 데이터를 Base64 URL-safe Encode 를 통해 인코딩하여 직렬화한 것입니다.
토큰 내부에는 위변조 방지를 위해 개인키를 통한 전자서명도 들어있습니다.
따라서 사용자가 JWT 를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치게 됩니다.
검증이 완료되면 요청한 응답을 돌려줍니다.
모바일 같은 플랫폼은 한 번 로그인을 한 이후에 다시 로그인하기가 불편한 단점이 존재했습니다.
한 번 로그인을 한 이후에 유지할 방법이 필요했는데 기존에 쿠키와 세션으로 구현하기에는 어려움이 있었습니다.
왜냐하면 쿠키나 세션의 인증은 로그인을 지속적으로 유지할 수가 없었습니다.
그래서 토큰기반 인증 방식을 사용하게 되었는데 최초에는 로그인을 하면 우리가 흔히 알고있는 30분에서 1시간 정도의 만료기간을 가진 액세스토큰을 발급 받아서 사용을 했습니다.
근데 이렇게 로그인이 자주 풀리면 토큰기반 방식을 사용하는데 의미가 없고 세션과의 차이도 별로 없었습니다.
그래서 여기서 우리가 또 알 수 있는 긴 수명의 Refresh Token이라는 것이 만들어졌고 이 토큰은 긴 만료기간을 가지며 리프레시 토큰으로 액세스 토큰이 만료 되었을때 재발급 받는 용도로 사용하게 되었습니다.
하지만 여기서 문제가 생겼는데 최초에는 이 토큰을 클라이언트와 서버가 둘다 들고 있는 상태에서 서버가 토큰을 검증하는(인증 서버) 역할 까지 했는데 문제는 모바일 트래픽이 폭발적으로 늘어나면서 서버에 부하가 생겼습니다.
그래서 원래 엑세스 토큰을 가지고 서버가 인증 서버를 매번 거치지 않고 조금 더 쉽게 인증할 수 있는 방법이 무엇이 있을까 고민을 하게 되었습니다.
그리고 그 해답으로 JWT토큰이라는 것이 탄생하였고 JWT 토큰의 특성은 키를 서버가 가지고 있고 굳이 인증서버로 갈 필요없이 토큰이 유효한지 판별하는 것입니다.

JWT는 헤더(Header), 페이로드(Payload), 서명(Signature) 세 부분으로 나눠져있습니다.
이들 각각은 Base64Url로 인코딩되어 .을 사용하여 으로 구분됩니다.
Header는 JWT에서 사용할 해시 알고리즘 종류와 토큰 타입이 있습니다.
Payload는 서버에서 첨부한 사용자 권한 정보와 데이터가 담겨있습니다.
Signature에는 Header, Payload URL-safe Encode를 한 이후 Header 에 명시된 해시함수를 적용하고, 개인키(Private Key)로 서명한 전자서명이 담겨있습니다.
헤더는 토큰의 유형(JWT)과 서명에 사용할 알고리즘이 들어있습니다.
{
"alg": "HS256", // 서명 알고리즘 (HMAC SHA-256)
"typ": "JWT" // 토큰 타입 (JSON Web Token)
}
alg 대표적으로 RS256(공개키/개인키)와 HS256(비밀키/대칭키)가 있습니다.
typ 토큰의 타입입ㄴ다.
페이로드는 토큰에 담길 실제 데이터를 포함합니다.
여기에는 토큰에서 사용할 정보의 조각인 클레임(claims)이라 불리는 정보들이 포함됩니다.
대표적으로 등록된(Registered) 클레임, 공개(Public) 클레임, 비공개(Private) 클레임으로 나뉩니다.
{
"sub": "1234567890", // subject: 사용자 ID 또는 주체
"name": "John Doe", // 사용자 이름
"iat": 1516239022, // issued at: 발행 시간 (Unix timestamp)
"exp": 1516242622 // expiration: 만료 시간 (Unix timestamp)
}
클레임(Claims)
인증 및 권한 부여와 관련된 정보를 나타내는 키-값 쌍입니다.
토큰에 저장된 데이터의 본문을 구성하고, 주로 인증된 사용자에 대한 정보를 전달하고 리소스 접근 권한을 부여하는데 사용합니다.
사용자가 정의할 수 있는 클레임 공개용 정보 전달을 위해 사용.
해당하는 당사자들 간에 정보를 공유하기 위해 만들어진 사용자 지정 클레임. 외부에 공개되도 상관없지만 해당 유저를 특정할 수 있는 정보들을 담는다
서명은 토큰의 무결성을 보장하기 위해 사용됩니다.
헤더와 페이로드를 합친 후 지정된 비밀 키와 서명 알고리즘을 사용하여 생성됩니다.
이를 통해, 데이터가 변조되지 않았음을 확인할 수 있습니다.
Header와 Payload는 단순 인코딩된 값이기 때문에 복호화 및 조작이 가능하지만
Signature은 서버에서 관리하는 비밀키를 사용하기 때문에 해당 키가 유출되지 않는 이상 복호화할 수 없습니다.
그래서 Signature은 토큰의 위변조 여부를 확인하는데 사용됩니다. (Check-sum)
- CRC
- 해시알고리즘
- 인증서버 부하 감소
JWT는 기본적으로 인코딩된 형태일 뿐 암호화되지 않습니다.
즉, 누구나 Base64Url로 디코딩하여 페이로드를 볼 수 있습니다
따라서 중요한 정보를 페이로드에 포함하지 않는 것이 좋습니다.
JWT는 자체적으로 상태를 저장하지 않기 때문에, 토큰 만료와 같은 보안 관리가 어렵습니다.
만료된 JWT를 서버가 일일이 관리하지 않으면 보안 문제가 발생할 수 있습니다.
JWT는 페이로드에 많은 정보를 담을 수 있지만, 이로 인해 토큰의 크기가 커질 수 있습니다.
이는 네트워크 대역폭에 영향을 줄 수 있습니다.
앱에서 소셜 로그인을 할 때 JWT를 받아서 저장한 다음 헤더에 넣어서 API를 요청하는 방법은 알았지만 OAuth, OIDC, JWT에 관해서 깊게 알고있지는 않았습니다.
그래서 포스팅을 준비하며 해당 기술들이 생겨나게 된 역사와 더불어서 깊게 이해하게되는 계기가 된 것 같습니다
질문과 피드백은 언제나 환영합니다 🤗