🪪 부적절한 전자서명 확인 (JWT) KISA A-2 · 부적절한 전자서명 확인
서버가 JWT를 발급하지만 검증을 허술하게 합니다. 두 가지로 토큰을 위조합니다: alg를 "none"으로 바꾸기, 그리고 서버가 HMAC을 재계산하지 않고 payload를 base64 디코드만 하는 점을 악용해 내용을 변조하기. 올바른 검증기는 알고리즘을 고정하고 비밀키로 서명을 검증합니다.
0 · 정상 발급 토큰 (role = user)
로그인 서버는 HS256(base64(header).base64(payload), secret)로 서명합니다. 비밀키는 서버 밖으로 나가지 않습니다. 아래는 일반 사용자에게 발급된 진짜 토큰입니다.
① 취약한 검증기: 관리자 위조 VULNERABLE
취약한 서버는 헤더의 alg를 그대로 신뢰하고, HS256일 때도 HMAC을 재계산하지 않고 payload를 디코드만 합니다. payload를 수정해 토큰을 위조하면 그대로 통과합니다.
② 안전한 검증기: 알고리즘 고정 + HMAC 검증 SECURE
안전한 검증기는 alg=none을 거부하고, 허용목록(HS256)만 받아들이며, 비밀키로 header.payload의 HMAC을 재계산해 불일치 시 거부합니다 — 두 위조 모두 통과하지 못합니다.
③ 취약 vs 안전 코드
VULNERABLE
// Node.js — 서명 미검증 / alg 신뢰
function verify(token){
const [h,p,s] = token.split('.');
const header = JSON.parse(b64url(h));
// alg 를 그대로 신뢰
if(header.alg === 'none') return JSON.parse(b64url(p)); // 무서명 수용!
// HS256 여도 HMAC 재계산 없이 payload 디코드만
return JSON.parse(b64url(p)); // 변조 탐지 불가
}
SECURE
// 알고리즘 고정 + HMAC 재계산 검증
const ALLOW = ['HS256'];
function verify(token, secret){
const [h,p,s] = token.split('.');
const header = JSON.parse(b64url(h));
if(!ALLOW.includes(header.alg)) // alg=none 거부
throw new Error('alg not allowed');
const expected = HMAC256(h + '.' + p, secret);
if(!timingSafeEqual(s, expected)) // 서명 불일치 거부
throw new Error('bad signature');
return JSON.parse(b64url(p)); // 검증된 payload
}
방어 핵심: 알고리즘은 허용목록으로 고정(공격자의 alg를 읽지 말 것) ·
alg=none 거부 · 항상 서명을 재계산해 상수시간 비교 · 비밀키/공개키는 서버 측 보관 · exp/aud/iss도 검증.
⚠️ 교육용 시뮬레이션입니다. 여기서 쓰는 HMAC은 작은 결정적 mock이며 실제 HS256이 아닙니다. 실제 토큰이나 시스템은 사용하지 않습니다.