A07 認証の不備
正しくない相手を、正しい利用者として受け入れてしまう状態。破られる所は、パスワードの推測、ログインの前後のセッション、回復の手順、別の宛先に向けたトークンの4つに集まる。
要点
- パスワードだけの認証は、漏えいした組の使い回しで破られる。根本の策は多要素認証で、次が漏えい済みのパスワードを登録させないこと。
- ログインの成功、失敗、IDの有無で応答を変えない。違いがあれば、有効なIDを当てる手掛かりになる。
- ログインの時に、セッションIDを必ず新しく発行する。発行し直さなければセッション固定が通る。
- 再設定のトークンは、推測できない乱数、1回限り、期限付き、ハッシュで保管。リンクのホスト名を要求から取らない。
- トークンは署名に加えて、アルゴリズム、発行元、宛先を確かめる。
成立の条件
OWASPは、自動化された攻撃を許す、弱いパスワードや既定のパスワードを許す、知識による回復に頼る、パスワードを平文や弱いハッシュで保管する、多要素認証が無いか回避できる、セッションIDをURLに出す、ログインの後もIDを使い回す、ログアウトや無操作で無効にしない、資格情報の宛先と範囲を確かめない、を挙げる[1]。どれも、認証の結果として発行したものを、本来の相手以外が使える状態にする。
自動化された推測
クレデンシャルスタッフィングは、他のサービスから漏れたIDとパスワードの組を順に試す。パスワードスプレーは、よく使われるパスワードを多数のIDに少しずつ試し、IDごとのロックに掛からない[1][5]。
| 項目 | SP 800-63B-4 の要求 |
|---|---|
| 最小の長さ | パスワードだけで認証するなら15文字以上。多要素認証の一部としてだけ使うなら8文字以上[2] |
| 構成の規則 | 文字の種類を混ぜる規則を課してはならない[2] |
| 定期の変更 | 求めてはならない。漏えいの証拠があれば変更させる[2] |
| 拒否の一覧 | 設定と変更の時に、よく使われる語と漏えい済みの語の一覧と照合する[2] |
| 秘密の質問 | パスワードを選ぶ時に、知識による認証を求めてはならない[2] |
| 失敗の回数 | 1つの認証器への連続の失敗を100回以下に制限する[2] |
悪い例と直した例。応答の違いがIDの有無を教える
// 悪い例 IDが無いときと、パスワードが違うときで文言が違う。IDが無ければ照合をせず早く返る
if (!user) return res.status(404).json({ error: "このIDは登録されていません" });
if (!(await verifyPassword(user.hash, pw))) return res.status(401).json({ error: "パスワードが違います" });
// 直した例 文言と状態コードをそろえ、IDが無くても同じ重さの照合をする
const ok = await verifyPassword(user?.hash ?? DUMMY_HASH, pw);
if (!user || !ok) return res.status(401).json({ error: "IDかパスワードが違います" });
新規登録と再設定の画面でも、IDの有無で応答を変えない[1][6]。IDごとのロックだけではスプレーを止められず、逆に正しい利用者を締め出す道にもなる。IDごとと送信元ごとの制限を組み合わせ、失敗をすべて記録して警告につなぐ[1][5]。
セッション固定
ログインの前に発行したセッションIDを、ログインの後もそのまま使うと、前もってIDを知っている相手が、利用者のログインの後に同じIDで入れる。権限が変わる時のIDの発行し直しは必須とされる[3]。
悪い例と直した例。Express と express-session の架空のログイン
// 悪い例 ログイン前のセッションに、そのまま利用者を結び付ける
req.session.userId = user.id;
// 直した例 セッションを作り直してから結び付ける
req.session.regenerate((err) => {
if (err) return next(err);
req.session.userId = user.id;
res.json({ ok: true });
});
// セッションのCookie。Domain を付けず、サブドメインからの上書きと送信を絞る
Set-Cookie: __Host-sid=…; Path=/; Secure; HttpOnly; SameSite=Lax
- セッションIDは64ビット以上のエントロピーを持たせ、URLや隠しフィールドに出さない[1][3]。
__Host-の接頭辞のCookieは、Secureが要り、Domainを持てず、Path=/になる。隣のサブドメインから同じ名前で仕込まれにくくなる[3]。- ログアウト、無操作の期限、絶対の期限で、サーバーの側のセッションを無効にする。SP 800-63B-4 は AAL2 で、全体の期限を24時間以下、無操作の期限を1時間以下とすべきとする[2][3]。
- SSOでは、1つのアプリのログアウトで他のアプリのセッションが残る。シングルログアウトの有無を設計で決める[1]。
弱いパスワード再設定
回復の手順は、ログインより弱いことが多い。秘密の質問は、答えを知る人が複数いるため本人の証明にならない[1][6]。再設定のリンクは、それ自体が一時のパスワードになる。
悪い例と直した例。再設定のメールのリンク
// 悪い例 推測できる値を使い、リンクのホスト名を要求の Host ヘッダーから作る
const token = String(Date.now());
const link = `https://${req.headers.host}/reset?token=${token}`;
// 直した例 暗号論的な乱数。保管はハッシュ、期限付き、1回限り。ホスト名は設定から
const token = crypto.randomBytes(32).toString("base64url");
await db.query("INSERT INTO reset_tokens (user_id, token_hash, expires_at)" +
" VALUES ($1, $2, now() + interval '30 minutes')", [user.id, sha256(token)]);
const link = `${APP_ORIGIN}/reset?token=${token}`;
- Host ヘッダーからリンクを作ると、攻撃者が自分のホスト名を入れた要求で、利用者に届くリンクの行き先を変えられる。ホスト名は固定するか、信頼する一覧で確かめる[6]。
- 再設定のページに
Referrer-Policy: no-referrerを付け、トークンが外部へ漏れないようにする[6]。 - 再設定の後は自動でログインさせず、変更をメールで知らせ、残っているセッションを終わらせる[6]。
トークンの宛先を確かめない
署名が正しいことは、発行元の鍵で署名されたことしか示さない。同じ発行元が別のサービスに向けて出したトークンを受け入れれば、そのサービスの利用者として振る舞える。OWASPは JWT の aud、iss、スコープを確かめるよう求める[1]。RFC 7519 は、aud があるのに自分が含まれていなければ拒否しなければならないと定める[7]。
悪い例と直した例。jsonwebtoken での検証
// 悪い例 署名だけを確かめる
const claims = jwt.verify(token, publicKey);
// 直した例 アルゴリズムを固定し、発行元と宛先を確かめる
const claims = jwt.verify(token, publicKey, {
algorithms: ["ES256"],
issuer: "https://id.example.com",
audience: "https://api.example.com",
});
if (!claims.scope?.split(" ").includes("orders:read")) throw new Forbidden();
RFC 8725 は、アルゴリズムの検証、発行元と主体の検証、宛先の使用と検証を、JWTの実装の最善の慣行として挙げる[8]。
守り方
- 認証、本人確認、セッションの管理は、試験を経た既存の仕組みを使い、自前で作らない[1][4]。
- 重要な機能には多要素認証を求める。回復と予備の手段も、同じ強さにそろえる[1][9]。
- パスワードは SP 800-63B-4 の規則に合わせ、漏えい済みの一覧と照合する。保管は Argon2id などの、遅くした専用のハッシュにする[2][10]。
- ログインと権限の変化のたびにセッションIDを新しく発行し、サーバーの側で期限と無効化を管理する[3]。
- トークンは、アルゴリズム、発行元、宛先、スコープ、期限をすべて確かめてから使う[8]。
- 既定の資格情報を持ったまま出荷しない。管理者のアカウントは特に[1]。
次は緩和の策で、根本の直し方を済ませたうえで重ねる。
- 失敗の回数を、IDごとと送信元ごとに制限し、段階的に遅らせる。全部の失敗を記録し、攻撃が疑われる時に警告を出す[1]。
- パスワードマネージャーを使えるよう、貼り付けを禁じず、長い値を受け付ける[1]。
診断で見る所
- 応答の違い ログイン、新規登録、再設定で、IDがある時と無い時の文言、状態コード、応答の時間を比べる。
- 回数の制限 1つのIDへの連続の失敗と、多数のIDへの少ない失敗の両方で、制限と記録が働くか。
- セッションID ログインの前後で値が変わるか。ログアウトの後に古い値で要求が通らないか。URLやログに出ていないか。Cookieの属性。
- 再設定 トークンの長さと推測のしやすさ、2回使えないか、期限、Host ヘッダーを変えた時にリンクのホスト名が変わらないか。
- トークン 別の宛先向けのトークン、期限切れ、アルゴリズムを変えたヘッダーを受け入れないか。
- 試験の手引き OWASP WSTG の認証の試験と、セッション管理の試験の章[11][12]。IPAの安全なウェブサイトの作り方は、セッション管理の不備を1つの節で扱う[13]。
関連するCWE
A07には36個のCWEが対応づけられている[1]。このページの節に関わるものを挙げる。
| CWE | 名前 | このページの節 |
|---|---|---|
| CWE-287 | Improper Authentication | 全体の親 |
| CWE-307 | Improper Restriction of Excessive Authentication Attempts | 自動化された推測 |
| CWE-1391 | Use of Weak Credentials | 自動化された推測 |
| CWE-308 | Use of Single-factor Authentication | 自動化された推測 |
| CWE-384 | Session Fixation | セッション固定 |
| CWE-613 | Insufficient Session Expiration | セッション固定 |
| CWE-640 | Weak Password Recovery Mechanism for Forgotten Password | 弱いパスワード再設定 |
| CWE-1392 | Use of Default Credentials | 守り方 |
| CWE-798 | Use of Hard-coded Credentials | 守り方 |
参照
- A07:2025 Authentication Failures OWASP Top 10:2025
- SP 800-63B-4 Digital Identity Guidelines: Authentication and Authenticator Management NIST。2025年7月。3.1.1、3.2.2、再認証の期限
- Session Management Cheat Sheet OWASP Cheat Sheet Series
- Authentication Cheat Sheet OWASP Cheat Sheet Series
- Credential Stuffing Prevention Cheat Sheet OWASP Cheat Sheet Series
- Forgot Password Cheat Sheet OWASP Cheat Sheet Series
- RFC 7519 JSON Web Token IETF。4.1.3
- RFC 8725 JSON Web Token Best Current Practices IETF。3.1、3.8、3.9
- Multifactor Authentication Cheat Sheet OWASP Cheat Sheet Series
- Password Storage Cheat Sheet OWASP Cheat Sheet Series
- Authentication Testing OWASP Web Security Testing Guide
- Session Management Testing OWASP Web Security Testing Guide
- 安全なウェブサイトの作り方 IPA。改訂第7版 1.4 セッション管理の不備