# JWT攻撃の完全攻略【2026】alg:none・鍵ブルートフォース・アルゴリズム混同・jwk/jku/kid注入 — 公式ドキュメント忠実版

> JWT（JSON Web Token）攻撃の手法を、PortSwigger Web Security Academyに忠実に深掘り。署名検証の不備（任意署名の受理・alg:none）、HS256の弱い秘密鍵のhashcatブルートフォース、jwk/jku/kidヘッダ注入、RS256→HS256のアルゴリズム混同（鍵混同）攻撃の仕組みと、アルゴリズム固定・厳格な署名検証・jkuホスト許可リストによる根本対策までを、自分のラボ限定の実例で解説します。

- 公開日: 2026-06-28
- 著者: 友田 陽大
- タグ: セキュリティ, ホワイトハッカー, JWT, 脆弱性診断, Webセキュリティ
- URL: https://tomodahinata.com/blog/jwt-attack-techniques-alg-none-key-confusion-secret-cracking-guide
- カテゴリ: 実践Webハッキング技法
- 総合ガイド: https://tomodahinata.com/blog/web-application-hacking-techniques-methodology-owasp-portswigger-guide

## 要点

- JWTの安全性は『署名検証』に全依存する。攻撃の大半は検証の手抜きを突く：署名を検証していない、alg:none を受理する、検証ロジックが甘い——payloadを書き換えて他人や管理者になりすませる
- 弱い秘密鍵（HS256）はhashcatで割れる。`hashcat -m 16500 jwt.txt wordlist` で辞書攻撃。鍵が割れれば任意のトークンを正規に署名できる＝完全な認証バイパス。鍵は十分長くランダムに
- ヘッダ注入：jwk（公開鍵を埋め込む）/ jku（鍵セットURLを指す）/ kid（鍵ファイルを指す＝パストラバーサルやSQLi）。サーバーが攻撃者の鍵を信用してしまう設計が穴
- アルゴリズム混同（鍵混同）：RS256想定のサーバーに alg:HS256 のトークンを送り、公開RSA鍵を『HMACの秘密鍵』として署名させる。公開鍵は誰でも入手できるため、正規トークンを偽造できる
- 根本対策：サーバー側でアルゴリズムを固定（許可リスト）し、none を拒否、jku/kidの参照先を厳格に検証。実績ある最新ライブラリを正しく使い、鍵は長くランダムに、有効期限と失効も設計する

---

JWT（JSON Web Token）は、現代のSPAやマイクロサービスの認証・セッション管理で広く使われます。だからこそ、**JWTの検証を一つ間違えると、認証が丸ごと崩壊**します。[PortSwigger](https://portswigger.net/web-security/jwt) が言う通り「JWTベースの仕組みの安全性は、暗号署名に大きく依存」しており、攻撃の大半はその**署名検証の手抜き**を突きます。本記事は、その攻撃手法を公式に忠実に解説します。

> **絶対の前提:** 全手順は [合法ラボ](/blog/ethical-hacking-home-lab-kali-juice-shop-ctf-self-study-roadmap-guide) または許可スコープでのみ。他人のトークンの偽造・なりすましは、検証目的でも無許可なら不正アクセスです（→ [法律ガイド](/blog/ethical-hacker-law-japan-unauthorized-access-act-active-cyber-defense-disclosure-guide)）。地図は [ピラー](/blog/web-application-hacking-techniques-methodology-owasp-portswigger-guide)。JWTを含むトークン設計の基礎は [IDトークンとアクセストークンの違い](/blog/id-token-vs-access-token-oidc-oauth2-guide) も参照。

---

## 1. JWTの構造 — 3つのパート

JWTは `header.payload.signature` の3つをドット区切りで base64url エンコードしたものです。

```text
eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiJ3aWVuZXIifQ . <署名>
└─ header ────────┘   └─ payload ──────────┘
{"alg":"HS256"}        {"sub":"wiener","role":"user"}
```

- **header**：署名アルゴリズム（`alg`）などのメタ情報。
- **payload**：クレーム（誰か・権限・有効期限など）。**Base64は暗号化ではない**ので、誰でも中身を読める。
- **signature**：header+payload を鍵で署名したもの。**改ざん検出はここだけが頼り**。

つまり、**署名検証が甘ければ、payloadを `"role":"admin"` に書き換え放題**になります。

---

## 2. 署名検証の不備を突く

### 2.1 署名を検証していない / 任意署名の受理

開発者が「デコード」と「検証」を混同し、**署名を検証せずにpayloadを信用**しているケース。payloadを書き換えるだけで通ります。

```text
# payload の "sub":"wiener" を "sub":"administrator" に書き換えて送るだけで通る
# （Burp の JWT Editor 拡張でワンクリック改ざん→送信できる）
```

### 2.2 alg:none

JWS仕様には「署名なし（`alg:none`）」が存在します。サーバーがこれを受理すると、**署名を空にしてpayloadを自由に改ざん**できます。

```text
# header を {"alg":"none"} にし、署名部分を空にする
eyJhbGciOiJub25lIn0 . eyJzdWIiOiJhZG1pbmlzdHJhdG9yIn0 .
                                                       └─ 署名は空
```

単純な `none` が弾かれても、PortSwigger が指摘する通り **`None`・`nOnE`・予期しないエンコード**といった難読化で検証フィルタを抜けることがあります。

---

## 3. 弱い秘密鍵のブルートフォース（HS256）

HMAC系（HS256）は**「任意の文字列」を秘密鍵**にします。鍵が弱い/既定値だと、[hashcat](https://hashcat.net/) で割れます。

```bash
# JWTを辞書攻撃（-m 16500 が JWT モード）。割れた鍵が標準出力に出る
hashcat -a 0 -m 16500 jwt.txt /usr/share/wordlists/jwt.secrets.list

# 鍵が判明したら、任意のpayloadを「正規に」署名できる = 完全な認証バイパス
# 例: {"sub":"administrator"} を割れた鍵で署名して送る
```

**鍵が割れたら終わり**です。だから秘密鍵は**十分に長く・ランダム**でなければなりません（弱い既定鍵の流用が事故の典型）。応用暗号と鍵管理の原則は [パスワードハッシュ・暗号・鍵管理ガイド](/blog/password-hashing-argon2-encryption-key-management-applied-cryptography-guide) を参照。

---

## 4. ヘッダ・パラメータ注入（jwk / jku / kid）

JWT仕様は、署名検証に使う鍵をヘッダで指定する仕組みを持ちます。**サーバーがこれを無検証で信用すると、攻撃者の鍵で検証させられます。**

| パラメータ | 意味 | 攻撃 |
|---|---|---|
| **jwk** | 公開鍵をトークン内に直接埋め込む | 自分の鍵ペアで署名し、公開鍵をjwkに埋めて送る |
| **jku** | 鍵セット(JWK Set)のURLを指す | 攻撃者サーバーのJWK SetをURLに指定 |
| **kid** | 使う鍵のID（ファイル/DB参照に使われがち） | パストラバーサル（`../../dev/null`）やSQLiで鍵を操作 |

```text
# jwk 注入の例（header に自前の公開鍵を埋め、その秘密鍵で署名）
{"alg":"RS256","typ":"JWT","jwk":{"kty":"RSA","n":"<攻撃者の公開鍵>","e":"AQAB"}}

# kid をパストラバーサルに：既知内容のファイルを鍵に使わせ、署名を予測可能にする
{"alg":"HS256","kid":"../../../../../../dev/null"}
# /dev/null は空 → 鍵が空文字列になり、攻撃者が署名を作れる
```

---

## 5. アルゴリズム混同（鍵混同）攻撃 — RS256 → HS256

最も巧妙なのが**アルゴリズム混同（algorithm confusion / key confusion）**です。サーバーがRS256（非対称：公開鍵で検証・秘密鍵で署名）を想定しているのに、**検証時にalgを固定していない**と成立します。

仕組み：

1. RS256の**公開鍵は誰でも入手できる**（JWKエンドポイントや証明書から）。
2. 攻撃者は `alg` を **HS256（対称）に変えた**トークンを作る。
3. そのトークンを、**公開鍵の文字列を「HMACの秘密鍵」として**署名する。
4. サーバーは「HS256だな」と判断し、**手元の公開鍵をHMAC鍵として検証** → 一致してしまう。

```text
# 概念：本来 RS256 のサーバーへ、HS256 で署名したトークンを送る
# 署名鍵 = サーバーのRSA「公開」鍵（PEM文字列そのもの）
header  = {"alg":"HS256"}
payload = {"sub":"administrator"}
signature = HMAC-SHA256(base64(header)+"."+base64(payload), <RSA公開鍵PEM>)
```

公開鍵は秘密でないため、**攻撃者は正規に「見える」トークンを偽造できる**。これが鍵混同の怖さです（Burpの JWT Editor でPoC可能）。

---

## 6. 【守る側】根本対策

攻撃の構造がわかれば、防御は明快です。PortSwigger の推奨に沿います。

```ts
// ✅ 検証時にアルゴリズムを「固定」する（最重要）。none も他algも受け付けない
import { jwtVerify } from "jose";

const { payload } = await jwtVerify(token, publicKey, {
  algorithms: ["RS256"], // ← 許可リスト。これが無いと alg 混同/none を許す
  issuer: "https://auth.example",
  audience: "https://api.example",
});
```

設計原則：

- **アルゴリズムをサーバー側で固定**（許可リスト）。`alg:none` を拒否し、**トークンの`alg`を信用しない**。これだけで alg混同・none の大半を封じる。
- **検証は必ず行う**：「デコード」関数（`decode`）と「検証」関数（`verify`）を混同しない。
- **jku/jwk のホストを許可リスト**化（任意URLの鍵を信用しない）。**kid はパストラバーサル/SQLi 対策**を施す。
- **秘密鍵は長くランダム**に（HS256の弱鍵を避ける）。鍵のローテーションを設計。
- **実績ある最新ライブラリ**を、ドキュメント通りに使う（自前実装しない）。`jose` / `jsonwebtoken` 等。
- **有効期限（exp）と失効**：短い有効期限 + リフレッシュトークン + 失効リストで、漏洩時の被害を限定。
- **保存場所**：XSSでの窃取を避けるため、ブラウザでは `HttpOnly` Cookie を検討（→ [XSS対策](/blog/xss-attack-techniques-reflected-stored-dom-csp-bypass-guide)）。

認証基盤としての堅牢な設計（Cognito/Auth0/Clerk/Supabase Authの選定と、RS256のJWKS検証）は [認証プラットフォーム選定](/blog/auth-platform-selection-2026-cognito-auth0-clerk-supabase) と [Cognito JWT RS256検証](/blog/aws-cognito-jwt-rs256-verification-jwks-security-guide) で詳説しています。

---

## 7. まとめ

- **JWTの安全性は署名検証に全依存**：検証の手抜き（未検証・alg:none・甘い検証）が主な穴。
- **弱鍵はhashcatで割れる**：HS256の鍵は長くランダムに。
- **ヘッダ注入**：jwk/jku/kid をサーバーが無検証で信用すると攻撃者の鍵で通る。
- **アルゴリズム混同**：alg未固定だと、公開鍵をHMAC鍵にされ偽造される。
- **根本対策**：アルゴリズム固定 + 必ず検証 + jku/kid厳格化 + 長鍵 + 最新ライブラリ + exp/失効。

次は、ログイン機構そのものを狙う [認証の脆弱性の完全攻略](/blog/authentication-vulnerabilities-brute-force-2fa-bypass-attack-guide) へ。

---

### 参考（公式一次情報）

- [PortSwigger: JWT attacks](https://portswigger.net/web-security/jwt) ／ [Algorithm confusion](https://portswigger.net/web-security/jwt/algorithm-confusion)
- [OWASP: JSON Web Token for Java Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html) ／ [RFC 8725: JWT Best Current Practices](https://www.rfc-editor.org/rfc/rfc8725)
- [hashcat 公式](https://hashcat.net/) ／ [jose（JWTライブラリ）](https://github.com/panva/jose)
