쏭의 개발 블로그
JWT란 무엇인가? (+JWT 인증) 본문
[1] JWT란?
JWT는 RFC 7519 웹 표준으로 지정된 JSON 객체를 사용해서 토큰 자체에 정보를 저장하는 Web Token이다. 사용자의 인증 정보를 안전하게 전달하기 위한 방식이라고 생각하면 된다. JSON 데이터를 Base 64 URL-safe Encode를 통해 인코딩하여 직렬화한 것이며, 토큰 내부에는 위변조 방지를 위해 HMAC, RSA, ECDSA 등의 암호화 방식을 사용한 전자 서명도 들어있다. 여기서 Base URL-safe Encode는 일반적인 Base 64 Encode에서 URL에서 오류 없이 사용하도록 '+'와 '/'를 '-'와 '_'로 표현한 것이다.
JWT는 서버가 사용자에게 발급하며, 사용자는 이를 활용하여 서버와의 통신에서 자신을 입증할 수 있게 된다.
(1) 구조
aaaaaaa.bbbbbbb.ccccccccc
JWT는 위와 같이 Header, Payload, Signature의 형식으로 생성된다. 자세히 들여다보자.
Header
{
"alg": "ES256", // 서명 암호화 알고리즘
"typ": "JWT" // 토큰 유형
}
Header는 Signature를 해싱하기 위한 알고리즘 정보를 담고 있다. 토큰 유형과 사용중인 서명 알고리즘으로 구성되어있다. 위 예시에서는 토큰 유형을 JWT, 서명 암호화 알고리즘을 ES256(ECDSA)으로 지정하였다.
Payload
{
// 등록된 클레임
"iss": "https://dev-ssongu1.tistory.com", // 발행자
"sub": "kea75646535", // 제목
"iat": 1698765432, // 발급 시간
"aud": "https://dev-ssongu1.tistory.com" // 대상
"exp": 1698769032, // 만료 시간
// 개인 클레임
"nickname": "사용자의 닉네임",
"roles": "ROLE_USER"
}
Payload는 서버와 클라이언트가 주고 받는 시스템에서 실제 사용될 정보이다. Payload는 등록된 클레임과 개인 클레임 등으로 구성되어있다. 여기서 클레임이란 토큰에서 사용할 정보의 조각들로, 실제 JWT를 통해 알 수 있는 데이터이다.
- 등록된 클레임 (Registered Claims) : iss(발행자), iat(발급 시간), exp(만료 시간), sub(제목), aud(대상) 등으로 구성된다.
- 개인 클레임 (Private Claims) : 서로의 정보를 공유하기 위해 생성된 사용자 지정 클레임으로, 원하는 정보를 추가할 수 있다.
Signature
마지막으로 Signature는 토큰의 유효성 검증을 위한 문자열이다. Header, Payload, Key를 합쳐 암호화한 결과값으로, 대칭키 알고리즘 또는 비대칭키 알고리즘으로 서명할 수 있다. 구조는 (Header+Payload)와 서버가 가지고 있는 key값을 합친 것을 Header에서 정의한 알고리즘으로 암호화한 것이다.
⚠️ 여기서 주의할 점이 있다.
Header와 Payload값은 Base 64방식으로 인코딩되므로, 디코딩을 통해 누구나 정보를 알아낼 수 있다는 점이다. 인코딩은 데이터를 보호하는 방식이 아니라 단순히 데이터 형식을 변환하는 것이다. Payloadㄴ에는 민감한 정보를 담지 않거나, 담아야한다면 암호화 하는 것이 좋다.
Signature 값은 우리가 지정한 비밀키나 개인키를 알아야 구할 수 있다. Signature는 토큰의 위변조 여부를 확인하는 데 사용된다.
또한 JWT 토큰을 전송할 때는 HTTPS를 사용하여 통신 내용을 암호화하는 것이 좋다.
(2) 장단점
JWT는 다음과 같은 장단점들을 가지고 있다.
장점
1) 서버측 부하 감소 및 인증 저장소 불필요
- 서버는 서명 검증에 필요한 키만 알고 있으면 된다. 세션 방식과 같이 별도의 인증 저장소가 필요하지 않다.
2) 높은 확장성 및 분산 시스템에 용이
- 서버는 Stateless가 되어 서버 확장성이 우수해진다.
- 각 서버는 독립적으로 토큰의 유효성을 검증할 수 있으므로, 시스템 확장이 용이하다.
- 여러 개의 서버를 활용하는 대형 서비스 환경에서 접근 권한 관리가 효율적이다.
- 중앙의 인증 서버나 데이터 스토어에 대한 의존성이 없어 시스템 수평 확장이 용이하다.
3) Refresh Token 활용 시 높은 보안성
- Refresh Token을 사용하면 Access Token의 만료 시간을 짧게 설정하여 토큰 탈취 시 피해를 최소화 할 수 있다.
4) 플랫폼 독립성
- Base64 URL-safe Encoding을 사용하기 대문에 URL, Cookie, Header 등 다양한 방식으로 토큰 전송이 가능하다.
단점
1) Payload 크기 증가로 인한 네트워크 문제
- Payload 정보(클레임)이 많아지면 토큰 크기가 증가하여 네트워크 사용량이 증가할 수 있다. 데이터 설계 시 토큰 크기와 필요한 정보 간의 균형을 고려해야한다.
2) 민감한 정보 저장 불가
- 토큰이 클라이언트에 저장되고 Payload가 디코딩 가능하므로 중요한 데이터는 Payload에 저장할 수 없다.
3) 토큰 탈취 시 보안 취약성
- 서버에서 클라이언트의 토큰을 직접 조작할 수 없으므로 토큰 자체가 탈취당하면 대처가 어렵다.
- JWT를 서명하는 데 사용한 키가 손상되면 공격자가 위조 토큰을 생성할 수 있다.
4) 토큰 관리의 복잡성
- 세션이 없는 Stateless 방식이므로 로그아웃 시 토큰 관리가 어렵다.
- 만료될 때까지 유효하므로 사용자의 엑세스 권한을 즉시 취소해야하는 경우(ex: 보안 침해) 문제가 될 수 있다.
- 일반적으로 발급되면 변경할 수 없으므로 사용자의 역할이나 권한이 변경되면 토큰을 새로 발급받아야 한다.
[2] JWT를 이용한 인증
JWT 인증은 JWT 토큰을 HTTP헤더에 담아 서버가 클라이언트를 식별하는 방식이다. 웹 애플리케이션에서 사용자 인증 및 권한 부여에 사용된다.
JWT를 이용한 인증과정을 살펴보기에 앞서 Access Token과 Refresh Token이 무엇인지 알아보자.
(1) Access Token과 Refresh Token이란?
Access Token은 클라이언트가 가지고 있는 실제 유저 정보가 담긴 토큰이다. 클라이언트에서 요청이 오면 서버에서 해당 토큰이 있는 정보를 활용하여 사용자 정보에 맞게 응답을 진행한다.
Refresh Token은 새로운 Access Token을 발급해주기 위해 사용하는 토큰이다. 짧은 수명을 가지는 Access Token에게 새로운 토큰을 발급해주기 위해 사용한다. 보통 DB에 저장한다.
즉, Access Token은 접근을 위해, Refresh Token은 재발급을 위해 사용한다.
Refresh Token이 필요한 이유
Access Token만을 사용한 인증 방식은 문제를 가지고 있다. JWT 특성상 제 3자에게 탈취될 경우 즉시 무효화 맟 삭제가 불가능하므로 보안에 취약하다. Access Token이 탈취되면 토큰이 만료되기 전까지 토큰을 획득한 사람은 누구나 접근 권한을 가질 수 있다. 접근에 관여하는 Access Token에 유효기간을 짧게 설정하여 토큰 남용을 방지할 수 있으나, 이 경우에는 사용자가 로그인을 자주해야한다는 불편함이 있다.
이를 해결하기 위해 Refresh Token을 사용한다. Refresh Token은 긴 유효기간을 가지면서 Access Token이 만료되었을 때 새로 발급해줄 수 있도록 한다. 만료된 Access Token과 Refresh Token을 서버에 보내면, 서버가 Refresh Token을 검증하고 Access Token을 재발급 해준다. 이 방법을 사용하면 Access Token의 짧은 유효기간으로 인한 보안 취약점과 사용자 불편을 동시에 해결하여 안전하고 편리한 인증 환경을 제공할 수 있다.
(2) JWT 인증 과정
JWT를 활용한 인증 과정을 살펴보자.
- 클라이언트 ➡️ 서버 : 사용자가 ID와 PW를 입력하여 서버에 로그인 인증을 요청한다.
- 서버 : 클라이언트로부터 받은 요청으로 Header, Payload, Signature를 정의한다. 각각 Base64로 암호화하고 Key로 서명하여, JWT를 생성한다.
- 서버 ➡️ 클라이언트 : 로그인 요청 성공에 대한 응답으로 JWT를 쿠키나 응답 바디에 담아 클라이언트에 발급해준다.
- 나는 프로젝트에서 Refresh Token은 쿠키로 Access Token은 응답 바디에 담아 클라이언트에 발급해주었다.
- 클라이언트 : 서버로부터 받은 JWT를 로컬 스토리지나 쿠키 등에 저장한다.
- 클라이언트 ➡️ 서버 : HTTP/HTTPS Header에 Access Token을 담아 후속 요청을 보낸다.
- 서버 : 클라이언트가 보낸 Access Token이 내 서버에서 발행한 토큰인지 서명을 확인하여 검증한다. 유효한 경우 토큰에서 정보를 추출하여 사용자를 인증하고 허가한다.
- 서버 ➡️ 클라이언트 : Access Token이 유효한 경우 정보 응답을 준다.
❓토큰이 신뢰성을 가지는 이유는 무엇일까?
6번에서 클라이언트가 보낸 Access Token이 내 서버에서 발행한 토큰인지 확인하기 위해 서버는 유효성 검사를 한다.
1. 클라이언트가 보낸 Access Token의 Header와 Payload를 사용하여 Signature를 새롭게 생성한다.
2. 새롭게 생성한 Signature가 요청으로 받은 Signature와 일치하는지 확인한다.
일치하면 토큰이 유효하고, 불일치하면 토큰이 유효하지 않다고 판단하는 것이다.
이 때, 만약 다른 유저가 Payload를 임의로 수정했다고 가정하자.
그럼 토큰은 "Header + Payload + Signature" 구조에서 "Header + Payload*️⃣ + Signature"의 구조가 된다.
클라이언트는 " Header + Payload*️⃣ + Signature"을 서버에게 보내고, 서버는 이를 활용하여 검증하며 JWT를 생성한다.
서버가 생성한 JWT는 " Header + Payload*️⃣ + Signature*️⃣" 구조가 된다.
이 때, Signature 과 Signature*️⃣ 는 일치하지 않으므로 사용자 정보가 조작되었다는 것을 확인할 수 있다.
Access Token 만료 시 재발급 과정
Access Token이 만료되었을 때, Refresh Token을 활용해 Access Token을 재발급 받는 과정은 다음과 같다.
- 클라이언트 ➡️ 서버 : Access Token이 만료되면, Refresh Token으로 새로운 Access Token 발급 요청을 보낸다.
- 서버 : Refresh Token을 검증하고, 유효하면 Access Token을 다시 생성한다. 클라이언트의 Refresh Token와 DB의 Refresh Token이 일치하는 지 확인한 후, Access Token을 재발급한다.
- 서버 ➡️ 클라이언트 : 새로운 Access Token을 제공한다.
토큰 상태별 처리 방법
Access Token과 Refresh Token의 상태 별 처리 방법을 정리하면 다음과 같다.
- Access Token과 Refresh Token이 모두 만료 : 다시 로그인
- Access Token과 Refresh Token이 모두 유효 : 정상 처리
- Access Token은 만료, Refresh Token은 유효 : Refresh Token을 검증해서 Access Token 재발급
- Access Token은 유효, Refresh Token 만료 : 다시 로그인
(3) JWT 인증의 목적
JWT인증의 활용
- Stateless 인증 : 서버는 각 사용자에 대한 세션 상태를 유지할 필요가 없으므로 확장 및 부하 분산이 간소화된다.
- Single Sign-On(SSO) : 여러 애플리케이션에서 Single Sign-On을 활성화할 수 있다. 한 번 로그인하면 자격 증명을 다시 입력하지 않고도 다양한 서비스에 엑세스가 가능하다.
- Authorization(권한 부여) : JWT를 사용자 역할과 권한을 전달할 수 있으므로 서버가 세부적인 권한 부여 결정을 내릴 수 있다.
- 데이터베이스 쿼리 감소 : JWT에는 사용자 정보가 포함되어있어, 서버는 모든 요청에 대해 사용자 세부정보를 얻기 위헤 DB를 쿼리하지 않아도 된다.
- Cross-Origin Resource Sharing(CORS) : JWT는 HTTP헤더에 포함되어 웹 애플리케이션에서 안전한 CORS 통신에 사용할 수 있다.
JWT인증의 목적 : 서명
JWT인증의 핵심 목적은 정보 보호가 아닌 데이터의 무결성 및 위조 방지이다.
JWT는 Base64를 인코딩되어 쉽게 디코딩할 수 있으므로, 페이로드에는 비밀번호 등 민감한 정보를 넣는 것은 권장되지 않는다. JWT는 서명을 통해 데이터의 위변조 여부를 검증하는 데 사용된다. Signature에 사용된 key가 노출되지 않는 이상 데이터를 위조해도 Signature 부분에서 바로 걸러진다.
참고자료
https://inpa.tistory.com/entry/WEB-📚-JWTjson-web-token-란-💯-정리
https://inpa.tistory.com/entry/WEB-📚-Access-Token-Refresh-Token-원리-feat-JWT
https://medium.com/@codealfi/understanding-jwt-authentication-benefits-and-limitations-3c388dba172e
'Back-end' 카테고리의 다른 글
Soft Delete(논리 삭제)와 Hard Delete(물리 삭제) (0) | 2025.03.08 |
---|---|
OAuth 2.0 (0) | 2025.02.23 |
XSS와 CSRF 공격 (2) | 2025.02.16 |
HTTP method (GET&POST) (0) | 2023.01.31 |
Redirect와 Forward (0) | 2023.01.31 |