100% ブラウザ内完結計算 • サーバーログ完全ゼロ • 即時実行
Curious Techie開発者ツールボックス
Security, Identity & AuthIntermediate⏱️ 20 min challenge100% Client-Side

JWT暗号工学ラボ:クレーム改ざん・署名検証・アルゴリズム混乱攻撃の検証

Lab Objective

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文字でも変更されると、検証処理で不一致となり即座に拒絶されます。

Key Protocol Mechanics:
  • 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).
RFC Specifications:RFC 7519 (JSON Web Token)RFC 7515 (JSON Web Signature)RFC 7518 (JSON Web Algorithms)

3. Hands-On Lab Instructions

1

Open the interactive JWT builder island below.

2

Inspect the default Header (`{"alg":"HS256","typ":"JWT"}`) and Payload (`{"sub":"user_42","role":"user"}`).

3

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.

4

Attempt to verify the tampered token against the server secret key in the verification console.

5

Implement strict backend verification code that locks the allowed algorithms to `["HS256"]` and enforces `exp` validation.

4. Interactive Lab Workbench

Interactive JWT Construction & Tampering Sandbox
Client-Side Simulation•Active

Edit headers, claims, and signing secrets live. Observe instant Base64URL encoding, HMAC-SHA256 signing, and signature verification failures.

Assembled Compact Token
..
HMAC Signature Valid

Edit the payload to simulate tampering. Notice how the signature instantly updates!

5. Diagnostic Comprehension Questions

Q1

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).

Q2

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▼
Base64URL encoding is not encryption; try decoding the payload with `atob()` or JSON parse.
💡 Hint #2▼
A valid signature requires knowledge of the secret key; an attacker cannot forge a signature for modified claims without the secret.
💡 Hint #3▼
Check if your backend token parser explicitly validates the `exp` (expiration) timestamp.

7. Verified Production Solution

Solution Summary

JWTの確実な検証には、バックエンド側での秘密鍵による署名照合の徹底と、アルゴリズムヘッダー(alg)のホワイトリスト強制(noneの無条件排除)が不可欠です。

TYPESCRIPT CONFIGURATION
// 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;
}
Remediation Steps:
  • ✓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

決定論的技術因果関係 (GEO)

"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.