개발자공부일기
JWT 본문
1. HTTP 특성
HTTP는 인터넷 상에서 데이터를 주고 받기 위한 서버/클라이언트 모델을 따르는 프로토콜입니다. 클라이언트가 서버에게 요청을 보내면 서버는 응답을 보냄으로써, 데이터를 교환합니다. HTTP는 비연결성 및 무상태성 이라는 특징을 가지고 있습니다. HTTP는 요청에 대한 응답을 처리하게 되면 연결을 끊어 버립니다. 따라서 클라이언트에 대한 이전의 상태 정보 및 현재 통신의 상태가 남아있지 않습니다.
서버가 다수의 클라이언트와 연결을 계속 유지한다면, 이에 따른 자원 낭비가 심해집니다. 비연결성 및 무상태성의 특징을 가진다면 불필요한 자원 낭비를 줄일 수 있다는 장점이 있습니다.
그러나 서버는 클라이언트를 식별할 수 없다는 단점 또한 존재합니다. 로그인을 하더라도 다음 요청에서는 해당 클라이언트를 기억하지 못해, 또 로그인을 해야하는 문제가 발생합니다. 브라우저에서 새로고침을 누를 때마다 로그인을 해야하는 것입니다.
하지만 우리가 사용하고 있는 웹 사이트들의 경우, 한 번 로그인 하면 다시 로그인할 필요 없이 여러 페이지를 돌아다니며 다양한 기능들을 이용할 수 있습니다. 심지어는 브라우저를 껐다 켜도 로그인이 유지가 되기도 하지요. 이는 HTTP의 비연결성 및 무상태성 특징을 보완한 기술인 Cookie와 Session 덕분입니다.
2. Cookie
쿠키란 클라이언트가 어떠한 웹사이트를 방문할 경우, 그 사이트가 사용하고 있는 서버를 통해 클라이언트의 브라우저에 설치되는 작은 기록 정보 파일을 일컫습니다.
- 서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 때, 클라이언트 측에 저장하고 싶은 정보를 응답 헤더의 Set-Cookie에 담습니다.
- 쿠키는 Key-Value 형식의 문자열입니다.
- 이후 해당 클라이언트는 요청을 보낼 때마다, 매번 저장된 쿠키를 요청 헤더의 Cookie에 담아 보냅니다.
- 서버는 쿠키에 담긴 정보를 바탕으로 해당 요청의 클라이언트가 누군지 식별할 수 있습니다.
2.1. 단점
- 보안에 취약합니다.
- 요청 시 쿠키의 값을 그대로 보냅니다.
- 유출 및 조작 당할 위험이 존재합니다.
- 쿠키에는 용량 제한이 있어 많은 정보를 담을 수 없습니다.
- 웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 브라우저간 공유가 불가능합니다.
- 쿠키의 사이즈가 커질수록 네트워크에 부하가 심해집니다.
3. Cookie & Session 기반 인증
쿠키를 통해 클라이언트 로그인 상태를 유지시킬 수 있었지만, 가장 큰 단점은 쿠키가 유출 및 조작 당할 위험이 존재한다는 것입니다. 개인정보를 HTTP로 주고 받는 것은 위험합니다.
세션은 비밀번호 등 클라이언트의 인증 정보를 쿠키가 아닌 서버 측에 저장하고 관리합니다.
- 서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 때, 인증 정보는 서버에 저장하고 클라이언트 식별자인 JSESSIONID를 쿠키에 담습니다.
- 이후 클라이언트는 요청을 보낼 때마다, JSESSIONID 쿠키를 함께 보냅니다.
- 서버는 JSESSIONID 유효성을 판별해 클라이언트를 식별합니다.
3.1. 장단점
- 쿠키를 포함한 요청이 외부에 노출되더라도 세션 ID 자체는 유의미한 개인정보를 담고 있지 않습니다.
- 그러나 해커가 이를 중간에 탈취하여 클라이언트인척 위장할 수 있다는 한계가 존재합니다.
- 각 사용자마다 고유한 세션 ID가 발급되기 때문에, 요청이 들어올 때마다 회원정보를 확인할 필요가 없습니다.
- 서버에서 세션 저장소를 사용하므로 요청이 많아지면 서버에 부하가 심해집니다
.그럼 쿠키와 마찬가지로 세션을 사용했을 때의 단점은 다음과 같습니다.
- 세션 저장소의 문제가 발생하면 인증 체계가 무너져 이전에 다른 인증된 유저 또한 인증이 불가해진다.
- stateful 하기 때문에 http의 장점을 발휘하지 못하고 scale out에 걸림돌이 생긴다.
- 세션 저장소가 필수적으로 존재하기 때문에 이를 사용하기 위한 비용이 든다.
- 세션 ID가 탈취되었을 경우 대처는 가능하지만 클라이언트 인척 위장하는 보안의 약점이 있을 수 있다.
- 사용자가 많아질수록 메모리를 많이 차지하게 된다.
- "매번" 요청 시 세션 저장소를 조회해야 하는 단점이 있다.
이 단점을 보완하기 위해 JWT로 데이터 압축 및 서명를 위하여 JWT를 사용합니다.
그래서 JWT란 무엇인가?
JSON 웹 토큰이란?
JSON 웹 토큰(JWT)은 JSON 객체로 당사자 간에 정보를 안전하게 전송하기 위한 컴팩트하고 독립적인 방식을 정의하는 개방형 표준( RFC 7519 )입니다. 이 정보는 디지털로 서명되었기 때문에 검증하고 신뢰할 수 있습니다. JWT는 비밀(HMAC 알고리즘 사용) 또는 RSA 또는 ECDSA를 사용하는 공개/비공개 키 쌍을 사용하여 서명할 수 있습니다 .
JWT는 당사자 간의 비밀을 제공하기 위해 암호화될 수 있지만, 서명된 토큰에 초점을 맞출 것입니다. 서명된 토큰은 그 안에 포함된 클레임의 무결성을 확인할 수 있는 반면, 암호화된 토큰은 다른 당사자에게 해당 클레임을 숨깁니다 . 토큰이 공개/비공개 키 쌍을 사용하여 서명되는 경우, 서명은 또한 개인 키를 보유한 당사자만이 서명한 사람임을 증명합니다.
JSON 웹 토큰을 언제 사용해야 하나요?
JSON 웹 토큰이 유용한 몇 가지 시나리오는 다음과 같습니다.
- 권한 부여 : 이것은 JWT를 사용하는 가장 일반적인 시나리오입니다. 사용자가 로그인하면 이후의 각 요청에 JWT가 포함되어 사용자가 해당 토큰으로 허용된 경로, 서비스 및 리소스에 액세스할 수 있습니다. Single Sign On은 오버헤드가 작고 여러 도메인에서 쉽게 사용할 수 있기 때문에 요즘 널리 JWT를 사용하는 기능입니다.
- 정보 교환 : JSON 웹 토큰은 당사자 간에 정보를 안전하게 전송하는 좋은 방법입니다. JWT는 서명될 수 있기 때문에(예: 공개/비공개 키 쌍 사용) 발신자가 자신이 말하는 사람인지 확인할 수 있습니다. 또한 서명은 헤더와 페이로드를 사용하여 계산되므로 콘텐츠가 변조되지 않았는지 확인할 수도 있습니다.
JSON 웹 토큰의 구조는 무엇입니까?
JSON 웹 토큰은 압축된 형태로 점( .)으로 구분된 세 부분으로 구성되며, 각 부분은 다음과 같습니다.
- 헤더
- 유효 탑재량
- 서명
따라서 JWT는 일반적으로 다음과 같습니다.
xxxxx.yyyyy.zzzzz
각 부분을 나누어 보겠습니다.
헤더
헤더는 일반적으로 두 부분으로 구성됩니다. 토큰 유형(JWT)과 사용되는 서명 알고리즘(HMAC SHA256 또는 RSA)입니다.
예를 들어:
{
"alg": "HS256",
"typ": "JWT"
}
그런 다음, 이 JSON은 Base64Url 로 인코딩되어 JWT의 첫 번째 부분을 형성합니다.
Payload
토큰의 두 번째 부분은 클레임을 포함하는 페이로드입니다. 클레임은 엔터티(일반적으로 사용자)와 추가 데이터에 대해 기재하고 있습니다. 클레임에는 registered , public , private 클레임의 세 가지 유형이 있습니다.
- Registerd claims : 이는 필수는 아니지만 권장되는 사전 정의된 클레임 세트로, 유용하고 상호 운용 가능한 클레임 세트를 제공합니다. 일부는 다음과 같습니다. iss (발급자), exp (만료 시간), sub (주체), aud (청중) 및 기타 등등..
- JWT는 압축적이기 때문에 클레임 이름은 세 글자로만 구성됩니다.
- Public claims: JWT를 사용하는 사람들이 자유롭게 정의할 수 있습니다. 하지만 충돌을 피하기 위해 IANA JSON Web Tocken Registry에서 정의 하거나 충돌 방지 네임스페이스를 포함하는 URI로 정의해야 합니다.
- Private claims : 이는 당사자 간에 정보를 공유하기 위해 작성된 맞춤형 클레임으로, 이를 사용하는 데 동의한 당사자 간에 정보를 공유하기 위해 작성되며 등록되지 않았거나 공개된 클레임이 아닙니다.
예시 페이로드는 다음과 같습니다.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
그런 다음 페이로드는 Base64Url 로 인코딩되어 JSON 웹 토큰의 두 번째 부분을 형성합니다.
서명된 토큰의 경우 이 정보는 변조로부터 보호되지만 누구나 읽을 수 있습니다. 암호화되지 않은 한 JWT의 페이로드 또는 헤더 요소에 비밀 정보를 넣지 마십시오.
서명
서명 부분을 만들려면 인코딩된 헤더, 인코딩된 페이로드, 비밀, 헤더에 지정된 알고리즘을 가져와서 서명해야 합니다.
예를 들어 HMAC SHA256 알고리즘을 사용하려는 경우 서명은 다음과 같은 방식으로 생성됩니다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
서명은 메시지가 전송 도중 변경되지 않았는지 확인하는 데 사용되며 개인 키로 서명된 토큰의 경우 JWT를 보낸 사람이 주장한 대로 본인인지 확인할 수도 있습니다.
모두 합치기
출력은 HTML 및 HTTP 환경에서 쉽게 전달할 수 있는 점으로 구분된 세 개의 Base64-URL 문자열이며, SAML과 같은 XML 기반 표준과 비교할 때 더 간결합니다.
다음은 이전 헤더와 페이로드가 인코딩되어 있고 비밀로 서명된 JWT를 보여줍니다.
JWT를 사용하고 이러한 개념을 실제로 적용하려면 jwt.io디버거를 사용하여 JWT를 디코딩, 검증 및 생성할 수 있습니다.
JSON 웹 토큰은 어떻게 작동하나요?
인증에서 사용자가 자격 증명을 사용하여 성공적으로 로그인하면 JSON 웹 토큰이 반환됩니다. 토큰은 자격 증명이므로 보안 문제를 방지하기 위해 각별한 주의를 기울여야 합니다. 일반적으로 토큰을 필요 이상으로 오래 보관해서는 안 됩니다.
보안의 부족으로 인해 브라우저 저장소에 민감한 세션 데이터를 저장해서는 안 됩니다.
사용자가 보호 된 경로 또는 리소스에 액세스하려고 할 때마다 사용자 에이전트는 일반적으로 Bearer 스키마를 사용하여 Authorization 헤더에서 JWT를 보내야합니다. 헤더의 내용은 다음과 같아야합니다.
Authorization: Bearer <token>
이것은 어떤 경우에는 stateless authorization 메커니즘 일 수 있습니다. 서버의 보호 경로는 인증 헤더에서 유효한 JWT를 점검하며, 현재 존재하는 경우 사용자는 보호 된 리소스에 액세스 할 수 있습니다. JWT에 필요한 데이터가 포함 된 경우 특정 작업에 대한 데이터베이스를 쿼리해야 할 필요성이 줄어들 수 있지만 항상 그렇지는 않습니다.
HTTP 헤더를 통해 JWT 토큰을 보내는 경우 너무 커지지 않도록 해야 합니다. 일부 서버는 헤더에서 8KB 이상을 허용하지 않습니다. 모든 사용자 권한을 포함하는 것과 같이 JWT 토큰에 너무 많은 정보를 포함하려는 경우 Auth0 Fine-Grained Authorization 과 같은 대체 솔루션이 필요할 수 있습니다 .
토큰이 헤더로 전송되면 Authorization쿠키를 사용하지 않으므로 CORS(Cross-Origin Resource Sharing)가 문제가 되지 않습니다.
다음 다이어그램은 JWT를 얻어서 API나 리소스에 액세스하는 방법을 보여줍니다.
- 애플리케이션 또는 클라이언트는 권한 부여 서버에 권한을 요청합니다. 이는 다양한 권한 부여 흐름 중 하나를 통해 수행됩니다. 예를 들어, 일반적인 OpenID Connect 호환 웹 애플리케이션은 권한 부여 코드 흐름을/oauth/authorize 사용하여 엔드포인트를 통과합니다 .
- 권한이 부여되면 권한 부여 서버는 애플리케이션에 액세스 토큰을 반환합니다.
- 애플리케이션은 액세스 토큰을 사용하여 보호된 리소스(API와 같은)에 액세스합니다.
서명된 토큰의 경우 토큰에 포함된 모든 정보가 사용자나 다른 당사자에게 노출된다는 점에 유의하세요. 그들이 변경할 수 없더라도요. 즉, 토큰에 비밀 정보를 넣으면 안 됩니다.
그리고 아까 일반적으로 토큰을 필요 이상으로 보관하면 안된다고 했는데 그래서 우린AccessToken과 RefreshToken을 사용한다.
AccessToken과 RefreshToken을 사용
사용자가 로그인을 할 때에 AccessToken과 함께 그에 비해 긴 만료 시간을 갖는 RefreshToken을 클라이언트에 함께 발급합니다. 주로 AccessToken은 30분 내외, RefreshToken은 2주에서 한달 정도의 만료 기간을 부여합니다.
클라이언트는 AccessToken이 만료되었다는 오류를 받으면 따로 저장해두었던 RefreshToken을 이용하여 AccessToken의 재발급을 요청합니다. 서버는 유효한 RefreshToken으로 요청이 들어오면 새로운 AccessToken을 발급하고, 만료된 RefreshToken으로 요청이 들어오면 오류를 반환해, 사용자에게 로그인을 요구합니다.
AccessToken은 서버에 따로 저장해 둘 필요가 없지만, RefreshToken의 경우 서버의 stroage에 따로 저장해서 이후 검증에 활용해야 합니다. 그러므로 RefreshToken을 이용한다는 것은 추가적인 I/O 작업이 필요하다는 의미이며, 이는 I/O 작업이 필요없는 빠른 인증 처리를 장점으로 내세우는 JWT의 스펙에 포함되지 않는 부가적인 기술입니다.
RefreshToken은 탈취되어서는 곤란하므로 클라이언트는 보안이 유지되는 공간에 이를 저장해두어야 합니다.
RefreshToken은 서버에서 따로 저장을 하고 있기 때문에 강제로 토큰을 만료시키는 것이 가능합니다.
장점
- 짧은 만료 기간을 사용 할 수 있기 때문에 AccessToken이 탈취되더라도 제한된 기간만 접근이 가능합니다.
- 사용자가 로그인을 자주 할 필요가 없습니다.
- RefreshToken에 대한 만료를 강제로 설정 할 수 있습니다.
단점
- 클라이언트는 AccessToken의 만료에 대한 연장 요청을 구현해야 합니다.
- 인증 만료 기간의 자동 연장이 불가능합니다.
- 서버에 별도의 storage를 만들어야 합니다.
요약
JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화시킨 JSON 토큰을 의미합니다.
토큰은 세션과는 달리 서버가 아닌 클라이언트에 저장되기 때문에 메모리나 스토리지 등을 통해 세션을 관리했던 서버의 부담을 덜 수 있습니다.
이때 JWT(Acceess Token)를 HTTP 헤더에 실어 보내는 방식으로 서버가 클라이언트를 식별합니다.
일반적으로 JWT를 이용하는 순서는 가장 먼저로써 클라이언트 사용자가 아이디, 패스워드를 통해 웹서비스를 인증한 후 서버에서 서명된 JWT를 생성하여 클라이언트에 응답으로 돌려줍니다. 이후 클라이언트가 서버에 추가적인 데이터를 요구할 때 JWT를 HTTP Header에 실어 보냅니다. 서버에서는 클라이언트로부터 온 JWT를 검증 후 완료되면 요청한 응답을 돌려줍니다.
따라서 사용자가 JWT를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치게 되며 검증이 완료되면 요청한 응답을 돌려줍니다.
이때 Access Token만을 통한 인증 방식을 사용한다면 만약 해당 토큰이 제3자에게 탈취당할 경우 보안에 취약하다는 문제가 있습니다.
그러나 JWT는 발급 후 삭제가 불가능하므로 접근에 관여하는 토큰에 유효시간을 부여하는식으로 탈취 문제에 대응 해야 합니다.
토큰의 유효기간을 짧게 하면서 효과적인 방법은 Refresh Token을 함께 사용하는 것입니다.
로그인시 Access Token과 Refresh Token을 함께 발급하고, Access Token이 만료되면 사용자는 Refresh Token과 Access Token을 함께 서버로 보냅니다. 서버는 Access Token 조작여부를 체크하고 Refresh Token과 미리 저장해놓은 사용자 DB의 Refresh Token을 비교하여 두 개가 동일하고 유효기간도 남아있다면 새로운 Access Token을 발급해줍니다.
다른 API에서 호출시에는 req header에 Authorization 값으로 Bearer를 명시하여 JWT를 넣고 인증처리를 합니다.
'Javascript' 카테고리의 다른 글
깊은 복사와 얕은 복사 (0) | 2025.02.10 |
---|---|
Express (0) | 2025.02.07 |
Arrow Function(화살표 함수) (0) | 2025.02.05 |
async/await (0) | 2025.02.05 |
Promise (0) | 2025.02.04 |