JWT 알고리즘 ‘none’ 공격: 코드 한 줄에 숨은 취약점

발행: (2025년 12월 31일 오후 02:53 GMT+9)
3 min read
원문: Dev.to

Source: Dev.to

공격 시연

// ❌ This looks fine...
const decoded = jwt.verify(token, secret, {
  algorithms: ['HS256', 'none'], // 💀 The vulnerability
});

1. 공격자가 유효한 JWT를 획득

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTYiLCJyb2xlIjoidXNlciJ9.
signature_here

2. 헤더를 none 알고리즘으로 수정

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.
eyJzdWIiOiIxMjM0NTYiLCJyb2xlIjoiYWRtaW4ifQ.
// No signature needed!

3. "none"이 알고리즘 목록에 포함돼 있어 서버가 허용

공격자는 이제 관리자 권한을 가짐.

알려진 CVE

CVELibraryIssue
CVE-2015-2951jwt‑simple알고리즘 혼동
CVE-2016-10555jose2gonone 알고리즘 우회
CVE-2018-0114node‑jose키 혼동

안전한 설정 예시

// ✅ Explicitly whitelist algorithms
const decoded = jwt.verify(token, secret, {
  algorithms: ['HS256'], // Only what you use!
});
// ❌ Dangerous: allowing "none"
jwt.verify(token, secret, { algorithms: ['none'] });
// ✅ Safe: explicit RS256 verification
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
// ❌ Dangerous: brute‑forceable secret
jwt.sign(payload, 'password123');
// ✅ Safe: strong secret (256+ bits)
jwt.sign(payload, process.env.JWT_SECRET);
// ❌ Dangerous: token never expires
jwt.sign({ userId: 123 }, secret);
// ✅ Safe: short expiration
jwt.sign({ userId: 123 }, secret, { expiresIn: '1h' });
// ❌ Dangerous: password in token (tokens can be decoded!)
jwt.sign({ userId: 123, password: 'secret' }, key);
// ✅ Safe: only IDs in payload
jwt.sign({ userId: 123 }, key);

JWT 보안을 위한 ESLint 플러그인

eslint-plugin-jwt 규칙 세트는 모범 사례를 강제하고 흔히 발생하는 실수를 잡아줍니다.

규칙 개요

RuleCWEWhat it catches
no-algorithm-noneCWE‑347알고리즘 "none" 허용
no-algorithm-confusionCWE‑327RS/HS 혼동 공격
no-weak-secretCWE‑326무차별 대입 가능한 비밀키
no-hardcoded-secretCWE‑798코드에 하드코딩된 비밀
no-sensitive-payloadCWE‑312토큰에 포함된 개인식별정보(PII)
require-expirationCWE‑613exp 클레임 누락
require-algorithm-whitelistCWE‑327명시적 알고리즘 미지정
require-issuer-validationCWE‑345iss 검증 누락
require-audience-validationCWE‑345aud 검증 누락
no-decode-without-verifyCWE‑347jwt.decode() 오용
require-issued-atCWE‑613iat 클레임 누락
require-max-ageCWE‑613verifymaxAge 미설정
no-timestamp-manipulationCWE‑345시계 왜곡 공격

예시 Lint 오류

src/auth.ts
  15:3  error  🔒 CWE-347 CVSS:9.8 | JWT algorithm 'none' is allowed
               Risk: Attackers can forge tokens without a signature
               Fix: Remove 'none' from algorithms: ['HS256']

설치 방법

npm install eslint-plugin-jwt
// .eslintrc.js
import jwtPlugin from 'eslint-plugin-jwt';

export default [jwtPlugin.configs.recommended];

13개의 규칙. 완전한 JWT 보안. 오탐 제로.
⭐ GitHub에 별을 달아 주세요.

행동에 옮기세요

🚀 지금 바로 JWT 설정을 점검해 보세요. 알고리즘 목록에 "none"이 포함돼 있나요?

GitHub | LinkedIn

Back to Blog

관련 글

더 보기 »

세션 토큰 vs JWT: 잘못된 이분법

JWT와 세션 – 이것이 양자택일이 아닌 이유 논쟁은 끝이 없지만, 어느 쪽을 선택할 필요는 없습니다. 하이브리드 접근 방식이 당신에게 b...