JWT暗号工学ラボ:クレーム改ざん・署名検証・アルゴリズム混乱攻撃の検証
Base64URLエンコードと暗号学的署名の根本的な違いを理解し、JWT検証における代表的な実装脆弱性を診断・防止するスキルを習得する。
1. Production Scenario & Incident
本番APIは Authorization: Bearer ヘッダーでJWTトークンを受け取っています。攻撃者がトークンのペイロード内の {"role": "user"} を {"role": "admin"} に改ざんして特権昇格を試みています。改ざんされたトークンを検知・遮断してください。
2. Architectural Background & RFC Standards
RFC 7515 (JWS) は、ヘッダー・ペイロード・署名の3部構成です。署名はヘッダーとペイロードを結合し秘密鍵を用いて暗号学的ハッシュ(HS256等)を算出します。ペイロードが1文字でも変更されると、検証処理で不一致となり即座に拒絶されます。
- Compact Serialization: `Base64URL(Header) . Base64URL(Payload) . Base64URL(Signature)`
- Cryptographic Signature: For HS256, `HMACSHA256(HeaderB64 + "." + PayloadB64, secret)`. Modifying any character in the header or payload invalidates the signature.
- The "alg: none" Vulnerability: Unsanitized verification libraries historically allowed tokens specifying `"alg": "none"` to bypass signature verification entirely.
- Standard Registered Claims: `iss` (Issuer), `sub` (Subject), `aud` (Audience), `exp` (Expiration Time), `nbf` (Not Before), and `iat` (Issued At).
3. Hands-On Lab Instructions
Open the interactive JWT builder island below.
Inspect the default Header (`{"alg":"HS256","typ":"JWT"}`) and Payload (`{"sub":"user_42","role":"user"}`).
Modify `"role": "user"` to `"role": "admin"` in the payload editor. Notice how the middle Base64URL segment changes instantly while the signature remains calculated from the original.
Attempt to verify the tampered token against the server secret key in the verification console.
Implement strict backend verification code that locks the allowed algorithms to `["HS256"]` and enforces `exp` validation.
4. Interactive Lab Workbench
Edit headers, claims, and signing secrets live. Observe instant Base64URL encoding, HMAC-SHA256 signing, and signature verification failures.
Edit the payload to simulate tampering. Notice how the signature instantly updates!
5. Diagnostic Comprehension Questions
Are JSON Web Tokens (JWS compact format) encrypted by default?
Reveal Answer & Explanation →
✓ Correct Answer: No, standard JWS tokens are merely signed and Base64URL-encoded. Anyone can decode and inspect the payload claims in plaintext.
JWS (JSON Web Signature) provides integrity and authenticity, NOT confidentiality. The payload is encoded using Base64URL, which is trivially decodable by anyone. Confidential data must be encrypted using JWE (RFC 7516).
How should a secure backend verify incoming JWT signatures to prevent algorithm confusion attacks?
Reveal Answer & Explanation →
✓ Correct Answer: Explicitly restrict the verification function to a hardcoded whitelist of allowed algorithms (e.g., algorithms: ["HS256"]).
Never allow an unverified token header to dictate which algorithm the verification library uses. Explicitly pass allowed algorithms (such as `algorithms: ["HS256"]`) to prevent attackers from submitting "none" or asymmetric public keys disguised as symmetric HMAC secrets.
6. Progressive Diagnostic Hints
💡 Hint #1▼
💡 Hint #2▼
💡 Hint #3▼
7. Verified Production Solution
JWTの確実な検証には、バックエンド側での秘密鍵による署名照合の徹底と、アルゴリズムヘッダー(alg)のホワイトリスト強制(noneの無条件排除)が不可欠です。
// Production Node.js JWT Verification with jsonwebtoken
import jwt from 'jsonwebtoken';
const JWT_SECRET = process.env.JWT_SECRET_KEY; // High-entropy secret (>= 256 bits)
export function verifyUserToken(authHeader: string | undefined) {
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new Error('Missing or malformed Authorization header');
}
const token = authHeader.slice(7);
// SECURE VERIFICATION:
// 1. Explicitly whitelist allowed algorithms (rejects "none" or RS256 confusion)
// 2. Enforces expiration (exp) and expected issuer (iss)
const decoded = jwt.verify(token, JWT_SECRET, {
algorithms: ['HS256'], // NEVER let the header dictate the algorithm!
issuer: 'https://auth.curious-techie.com',
audience: 'https://api.curious-techie.com',
clockTolerance: 5 // 5 seconds tolerance for clock skew
});
return decoded;
}- ✓1. Extract the token strictly from the `Authorization: Bearer <token>` header.
- ✓2. Explicitly specify `algorithms: ["HS256"]` in the verification call so the parser rejects tokens declaring `"alg": "none"` or mismatched asymmetric keys.
- ✓3. Validate standard claims: `exp` (reject expired tokens), `iss` (expected auth server), and `aud` (expected API audience).
- ✓4. Store the HMAC secret in a secure environment variable with at least 32 bytes (256 bits) of cryptographic entropy.
8. In-Depth Technical Explanation
"If an adversary modifies any bit of the JWT payload without the symmetric HMAC secret, the mathematical digest HMAC(Header + "." + Payload, Secret) will diverge from the transmitted signature; consequently, any compliant verification engine deterministically throws a JsonWebTokenError / SignatureVerificationFailed exception."
Underlying Root Cause Analysis
The cryptographic security of a JWT rests entirely on its third segment: the signature. In HMAC-SHA256, the server hashes the concatenated header and payload strings using a secret key only known to the server. If an adversary alters any claim in the payload, the resulting cryptographic hash changes completely (due to the avalanche effect). If the server verifies the signature before reading claims, the forgery is immediately rejected.
Production Engineering Best Practices
- →Use strong secrets with at least 256 bits of entropy generated from `/dev/urandom`.
- →Always set short token lifespans (5 to 15 minutes) combined with secure, HTTP-only refresh tokens.
- →Never store sensitive passwords, API keys, or personally identifiable information (PII) in JWT payloads.
Common Pitfalls to Avoid
- ✕Using `jwt.decode()` instead of `jwt.verify()`, which parses claims without checking the cryptographic signature.
- ✕Using weak, dictionary-word secret keys like "secret" or "password" that can be brute-forced offline in seconds.
- ✕Relying on JWT blacklist caches in relational databases, defeating the stateless advantage of tokens.
関連技術エコシステムと次の学習ステップ
関連技術解説記事
JWT vs Session-Based Authentication: Architecture & Performance
Compare database session lookups with stateless JWT verification across scalability and revocation.
Troubleshooting: JWT Works in Postman but Fails in Browser
Diagnose and remediate CORS header stripping, HttpOnly cookie constraints, and preflight authorization failures.
関連する技術Q&A
Can a JWT Be Decrypted by Anyone?
Answer: Yes, standard JSON Web Tokens (JWS) are signed, not encrypted. Anyone can decode and inspect the payload without the secret key.
Why Shouldn't You Store JWTs in LocalStorage?
Answer: Storing tokens in localStorage exposes them to instant exfiltration by any Cross-Site Scripting (XSS) vulnerability.
Can an Expired JWT Token Still Be Verified?
Answer: Yes, the cryptographic signature remains mathematically valid, but compliant libraries reject the token because exp is checked after signature verification.
開発者向け規格・仕様リファレンス
次のステップへ進む
JWT Interactive Tampering & Validation Lab
Put your theoretical understanding into practice by tampering with claims and testing signature rejection in the live sandbox.