A04 暗号化の不備
暗号が無い、弱い、使い方が誤っている、の3つ。破られるのは暗号の数学より、鍵、乱数、モード、パラメータ、検証の省略のほうだ。
要点
成立の条件
機密のデータが通る場所と置かれる場所の、どこか1つで守りが外れれば成り立つ。通信だけ、保存だけを見ていると漏れる。
- 暗号が無い 平文の通信、平文の保存、HTTP を強制しない。FTP や STARTTLS のような暗号化されない手順を OWASP は使わないよう求める[1]。
- 弱い MD5、SHA-1、CBC、PKCS #1 v1.5 のような避けるべき方式。弱い鍵、期限切れの鍵、格下げできる交渉[1]。
- 使い方が誤っている パスワードをそのまま鍵にする、暗号用でない乱数、IV の使い回し、ECB、認証の無い暗号化、証明書の連鎖を確かめない、暗号のエラーの差を外へ漏らす[1]。
型1 平文の通信と格下げ
OWASP の例は、全ページで TLS を強制しないサイトの通信を安全でない無線の網で見張り、HTTPS から HTTP に落として、セッションの Cookie を盗むものだ。盗んだ Cookie で本人として振る舞い、送金の宛先のような中身も書き換えられる[1]。
直した例。HTTP は転送だけにし、HTTPS の応答で HSTS を送る
# HTTPS の応答
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Set-Cookie: sid=...; Secure; HttpOnly; SameSite=Lax; Path=/
Cache-Control: no-store # 機密を返す応答だけ
- HSTS は、受け取った端末が以後その名前へ HTTP で送らないようにする仕組み[6]。最初の1回は守れないため、
preloadの一覧への登録で埋める。 - TLS は 1.3 を既定にし、互換のために 1.2 を残してよい。1.0 と 1.1 は RFC 8996 で廃止された[5][7]。OWASP は前方秘匿性のある暗号の組を求め、CBC の組を外すよう書く[1]。
- TLS は届いた後のデータを守らない。機密を返す応答は
Cache-Control: no-storeで端末と中継のキャッシュに残さない[5]。OWASP は CDN、Webサーバー、アプリのキャッシュも対象に挙げる[1]。 - 内部の通信で証明書の確かめを切る設定は、外からの格下げと同じ穴を内側に作る。CWE-296。
型2 速いハッシュでのパスワードの保存
ハッシュの表が漏れた後の、手元での総当たりの速さで決まる。OWASP は、塩の無いハッシュは事前計算の表で、塩があっても速いハッシュは GPU で破られ得ると書く[1]。
悪い例
const hash = crypto.createHash('sha256').update(password).digest('hex'); // 塩なし、速い
直した例。架空のライブラリの形で、Argon2id の最小の推奨値
const hash = await argon2id.hash(password, {
memoryKiB: 19456, // 19 MiB
iterations: 2,
parallelism: 1,
}); // 塩は利用者ごとに自動で作られ、結果に含まれる
const ok = await argon2id.verify(hash, input);
| 方式 | チートシートの最小の推奨 | 使う場面 |
|---|---|---|
| Argon2id | メモリ 19 MiB、反復 2、並列 1 | 第一の選択 |
| scrypt | N=2^17、r=8、p=1 | Argon2id が使えないとき |
| bcrypt | 作業係数 10 以上。入力は 72 バイトまで | 既存の仕組み |
| PBKDF2 | HMAC-SHA-256 で 600,000 回以上 | FIPS-140 への準拠が要るとき |
- 値は 2026年11月時点のパスワード保存のチートシート[3]。Argon2 の引数の定義は RFC 9106[8]。推奨は上がっていくので、ハッシュに方式と引数を含めて保存し、ログインの成功時に新しい引数で作り直せるようにする。
- bcrypt の 72 バイトの上限を越える入力は切り捨てられる。前処理で速いハッシュを挟む場合の落とし穴もチートシートにある[3]。
- IPA の「安全なウェブサイトの作り方」も、第2章でウェブサイトにおけるパスワードの取扱いに関する対策を扱っている[12]。
型3 暗号用でない乱数
セッションのID、パスワードの再設定のトークン、招待のURL、APIの鍵を、暗号用でない疑似乱数で作る。出力の一部から内部の状態を推し量り、次の値を当てられる。2025年版の A04 でよく見つかるCWEの多くがここに当たる[1]。
悪い例
const token = Math.random().toString(36).slice(2); // 暗号用でない。値の空間も狭い
const resetId = Date.now() + userId; // 予測できる
直した例。OS の暗号用の乱数から 256 ビット
const token = crypto.randomBytes(32).toString('base64url');
- 暗号用の乱数を使い、自分で種を与えない。OWASP は、近年のAPIは開発者が種を与えなくても安全で、低いエントロピーの種で上書きすると弱くなると書く[1]。
- トークンはデータベースにハッシュで保存し、期限と使い捨てを付ける。漏れた表から直接使えないようにする。
- 連番や時刻からIDを作る作りは、乱数でなくても予測できる。CWE-340。
型4 鍵とモードと署名の誤り
正しい方式でも、使い方で守りが消える。ECB は同じ平文のブロックを同じ暗号文にするので、模様が残る。IV を同じ鍵で使い回すと、GCM のような方式では機密性と完全性の両方が崩れる[9]。署名の方式をトークンの側に選ばせると、確かめを素通りされる。
悪い例。埋め込んだ鍵と ECB
const key = Buffer.from('0123456789abcdef'); // ソースに鍵
const c = crypto.createCipheriv('aes-128-ecb', key, null); // 模様が残り、改ざんも分からない
直した例。鍵は外から渡し、毎回新しい IV で認証つき暗号
const key = await kms.getDataKey('records'); // 架空の鍵の管理の呼び出し。32 バイト
const iv = crypto.randomBytes(12); // 同じ鍵で二度使わない
const c = crypto.createCipheriv('aes-256-gcm', key, iv);
const body = Buffer.concat([c.update(plain), c.final()]);
const tag = c.getAuthTag(); // iv と tag を暗号文と一緒に保存し、復号で確かめる
直した例。トークンの署名の方式をサーバーが決める
const claims = jwt.verify(token, publicKey, {
algorithms: ['ES256'], // ヘッダの alg に従わない
issuer: 'https://id.example.com',
audience: 'https://api.example.com',
});
- OWASP は、認証つき暗号を常に使い、IV は同じ鍵で二度使わず、鍵は暗号用の乱数で作り、パスワードを鍵にするなら鍵導出関数を通すよう求める[1]。GCM の仕様は NIST SP 800-38D[9]。
- 最も機密な鍵はハードウェアかクラウドの HSM に置く[1]。鍵の作成、保管、回転、廃棄は鍵の管理のチートシートにある[10]。
- JWT の扱いは RFC 8725 が定める。方式の検証、適した方式、発行者と宛先の確かめを求める[11]。役割をトークンから読むなら、ここが A01 の守りの前提になる。
- 復号の失敗の理由を外へ返し分けると、パディングオラクルの材料になる。OWASP は暗号のエラーと副チャネルの情報の漏れを挙げる[1]。
守り方
診断で見る所
許可を得た範囲で行う。範囲の決め方は 診断の許可と範囲 にある。手順は WSTG の弱い暗号の試験が詳しい[2]。
| 観点 | 見る所 | 当たりの兆候 |
|---|---|---|
| 通信 | TLS の版と暗号の組、証明書の連鎖、HTTP の受け口、HSTS | 1.0 か 1.1 が通る。CBC の組。HTTP のまま応答する。HSTS が無い |
| Cookie と キャッシュ | Set-Cookie の属性、機密の応答の Cache-Control | Secure が無い。機密がキャッシュされる |
| パスワード | 保存されたハッシュの形、ログインの処理のコード | MD5、SHA-1、SHA-256 の単発。塩が無いか共通 |
| 乱数 | トークンとIDを作るコード | Math.random、時刻、連番。固定の種 |
| 暗号化 | 暗号の呼び出しのモード、IV、鍵の出どころ | ECB、固定の IV、ソースや設定の鍵、認証の無い CBC |
| 署名 | JWT と署名つきデータの検証のコード | 方式をヘッダに従う。検証の無い読み取り。宛先を見ない |
| 証明書の確かめ | 外への HTTP の呼び出しの設定 | 確かめを切る指定が残っている |
関連するCWE
A04 に対応する32のうち、Webアプリでよく当たるもの[1]。
| CWE | 名前 | この頁の型 |
|---|---|---|
| CWE-319 | Cleartext Transmission of Sensitive Information | 型1 |
| CWE-523 | Unprotected Transport of Credentials | 型1 |
| CWE-757 | Selection of Less-Secure Algorithm During Negotiation | 型1 |
| CWE-296 | Improper Following of a Certificate's Chain of Trust | 型1 |
| CWE-916 | Use of Password Hash With Insufficient Computational Effort | 型2 |
| CWE-759 | Use of a One-Way Hash without a Salt | 型2 |
| CWE-330 | Use of Insufficiently Random Values | 型3 |
| CWE-338 | Use of Cryptographically Weak Pseudo-Random Number Generator | 型3 |
| CWE-340 | Generation of Predictable Numbers or Identifiers | 型3 |
| CWE-321 | Use of Hard-coded Cryptographic Key | 型4 |
| CWE-327 | Use of a Broken or Risky Cryptographic Algorithm | 型4 |
| CWE-323 | Reusing a Nonce, Key Pair in Encryption | 型4 |
| CWE-347 | Improper Verification of Cryptographic Signature | 型4 |
参照
- A04:2025 Cryptographic Failures OWASP Top 10:2025
- Testing for Weak Cryptography OWASP Web Security Testing Guide
- Password Storage Cheat Sheet OWASP Cheat Sheet Series
- Cryptographic Storage Cheat Sheet OWASP Cheat Sheet Series
- Transport Layer Security Cheat Sheet OWASP Cheat Sheet Series
- RFC 6797 HTTP Strict Transport Security IETF
- RFC 8996 Deprecating TLS 1.0 and TLS 1.1 IETF。2021年3月
- RFC 9106 Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications IRTF
- SP 800-38D Galois/Counter Mode and GMAC NIST
- Key Management Cheat Sheet OWASP Cheat Sheet Series
- RFC 8725 JSON Web Token Best Current Practices IETF
- 安全なウェブサイトの作り方 改訂第7版 IPA 情報処理推進機構
- NIST Releases First 3 Finalized Post-Quantum Encryption Standards NIST。2024年8月