기술인사이트

기술리포트

JWT 개요와 보안 처리
1. 서론
JWT(JSON Web Token)는 클라이언트-서버 구조에서 인증과 인가를 수행하기 위해 널리 사용되는 토큰 기반 인증 방식이다.
특히 마이크로서비스 아키텍처, 모바일 앱, 싱글 페이지 애플리케이션(SPA) 환경에서 그 활용도가 높아지고 있다.
JWT는 무상태(stateless) 구조와 서명 기반의 검증 메커니즘을 통해 세션 서버 없이 사용자 인증 정보를 검증할 수 있다는 장점이 있다.
그러나 JWT의 단순한 구조와 편리성은 동시에 다양한 보안 취약점을 수반한다.
대표적으로 서명 검증 누락, 알고리즘 우회 공격(alg=none), 키 관리 미숙, 페이로드 변조 등이 주요 위협으로 지적되고 있다.
이 글에서는 JWT의 구조와 장점을 소개하고, 다양한 보안 취약점 사례를 분석한 후, 안전한 구현을 위한 모범 사례와 대응 방안을 제시한다.

2. JWT 개요
2.1 JWT 구조
JWT는 세 부분으로 구성된다: Header, Payload, Signature.

[Header].[Payload].[Signature]
① Header: { "alg": "HS256", "typ": "JWT" }
② Payload: { "sub": "1234567890", "name": "John Doe", "admin": true }
③ Signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

2.2 알고리즘 종류

① 대칭키 알고리즘: HMAC-SHA256(HS256), HS384, HS512 등
② 비대칭키 알고리즘: RS256(RSA SHA-256), ES256(ECDSA) 등

비대칭키 방식은 공개키로 서명 검증이 가능하므로 다중 시스템에서 토큰 유효성 검증에 유리하다.

2.3 인증/인가 절차
JWT는 일반적으로 로그인 시 발급되어 클라이언트에 저장되고 이후 Authorization 헤더에 포함되어 요청과 함께 전송된다.
서버는 서명을 검증하고, 필요한 경우 클레임을 기반으로 인가를 수행한다.

2.4 JWT 인증 처리 흐름
① 사용자 로그인 요청: 사용자가 ID와 비밀번호를 서버로 전송
② 서버 인증 후 토큰 발급: 서버가 사용자 정보 확인 후 JWT를 생성하여 클라이언트에 전달
③ 클라이언트 저장: 토큰을 로컬 스토리지나 쿠키(HttpOnly) 등에 저장
④ 인증 요청: 클라이언트가 API 요청 시 Authorization 헤더에 Bearer 토큰으로 포함
⑤ 서버 검증 및 응답: 서버는 토큰 서명과 클레임을 검증한 후 요청 처리

POST /login HTTP/1.1
Content-Type: application/json
{
"username": "alice",
"password": "password123"
}
-->
HTTP/1.1 200 OK
{
"token": "eyJhbGciOi..."
}
GET /profile HTTP/1.1
Authorization: Bearer eyJhbGciOi...
3. JWT 보안 취약점 및 공격 사례
3.1 서명 검증 누락
개발자가 JWT를 해석할 때 서명을 검증하지 않고 payload만 파싱할 경우 보안 위협이 발생한다.
Python 예시

import jwt
# 서명 검증 없이 decode
decoded = jwt.decode(token, options={"verify_signature": False})
3.2 alg=none 공격

alg.을 'none'으로 설정한 후, 서명이 없는 JWT를 발급하여 서버 인증을 우회할 수 있다.

예시 토큰

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWRtaW4ifQ.
CVE-2015-9235 사례: Auth0의 JWT 라이브러리에서 none 알고리즘을 허용해 인증 우회가 가능했던 취약점이다.

3.3 알고리즘 혼동 공격
비대칭 알고리즘에서 공개키를 비밀키처럼 사용해 HS256 방식으로 서명된 JWT를 생성할 수 있다.
공격 시나리오
① 원래 알고리즘: RS256
② 공격자가 alg.을 HS256으로 변경 후 공개키로 HMAC 서명 → 검증 통과

<HMAC using SHA-256>


<RSA using SHA-256>

○ 페이로드 정보 노출
암호화되지 않은 payload에 이메일, 전화번호 등 민감 정보를 포함시켜 외부에 노출되는 사례가 빈발하게 발생하고 있다.

3.4 토큰 무효화의 어려움
Stateless 구조로 인해 로그아웃 시 서버에서 토큰을 무효화할 수 없어 보안 리스크가 발생한다.
- 대응 방법: Redis 등을 활용해 jti 기반의 토큰 블랙리스트 구현

4. 보안 대응 전략 및 모범 사례
4.1 서명 및 알고리즘 고정
서버는 alg를 클라이언트가 임의로 지정하지 못하도록 고정하고 화이트리스트를 명시한다.
예시

jwt.verify(token, publicKey, { algorithms: ['RS256'] });
4.2 안전한 키 관리
① AWS Secrets Manager, HashiCorp Vault 등 사용
② GitHub Actions에선 secrets 설정
③ 키 회전(rotation) 정책 도입

4.3 클레임 검증
exp(만료), iat(발급), iss(발급자), aud(수신자) 클레임을 검증하여 토큰 범위를 제한한다.
예시
const options = {
issuer: 'myapp',
audience: 'myusers',
maxAge: '15m'
};
jwt.verify(token, secretKey, options);
4.4 안전한 저장과 전송
① 쿠키 설정: HttpOnly, Secure, SameSite=Strict
② 전송: HTTPS 필수, HSTS 헤더 적용

4.5 로깅 및 모니터링
① JWT 검증 실패 로그 수집
② JWT 사용 이력 및 관리자 토큰 요청 추적
③ 도구: Snyk, OWASP ZAP, Burp Suite 등 사용

5. 결론
JWT는 효율적인 인증 시스템을 제공하지만, 구현 실수나 기본 보안 설정의 미흡으로 인해 심각한 취약점으로 이어질 수 있다.
앞서 살펴본 바와 같이 JWT 구조, 취약점 유형, 실제 공격 기법, 그리고 대응 전략을 종합적으로 고려하고 개발자와 보안 담당자는 다음의 5가지 원칙을 실천해야 한다.

- 강력한 알고리즘과 고정된 alg 설정
- 안전한 키 저장소와 주기적 키 교체
- 만료 시간, 발급자, 대상자 등의 클레임 철저 검증
- 토큰 저장 방식은 HttpOnly 쿠키, 전송은 HTTPS 설정
- 이상 행위에 대한 모니터링과 감사 체계 구축

JWT의 보안을 확보하는 것은 단순한 설정의 문제가 아니라, 전체 시스템 설계 및 운영 정책과 밀접한 관계를 맺는다.
앞으로는 JWE 기반의 암호화, 토큰 연계된 MFA, 정책기반 접근제어(PBAC) 등 추가 기술과의 통합도 적극 고려되어야 할 것이다.