結論(認証関連ページのno-store)
認証関連ページのno-storeとは、ログイン画面・管理画面・マイページなど認証に関わるページや人によって内容が変わるページの応答に、Cache-Control: no-storeを付けることです。no-storeは私的・共有を問わずあらゆるキャッシュへの保存を禁じる唯一の値で、no-cacheやprivateでは代わりになりません。付いていないと、共用端末のブラウザキャッシュや戻るボタン、プロキシ・CDNに個人情報や管理画面の内容が残り、別の利用者に見られる恐れがあります。
この記事のポイント
- 機微情報の保存を防ぐ目的に合うのは Cache-Control: no-store だけです。no-cache は「保存してよいが再検証する」という意味です。
- private は共有キャッシュを防ぐだけで、共用PCのブラウザキャッシュには残ります。
- 自治体には図書館・公民館・窓口の共用PCや庁内プロキシなど、キャッシュが第三者に見える環境が多くあります。
- セキュリティAIチェッカーは、トップページと巡回ページのうち /login・/admin・/mypage などのパスで2xxを返したページを調べ、no-store の無いページが1つでもあれば×と判定します。
- サイト全体に no-store を付けると表示が遅くなるため、認証系のパスに限定して設定します。
| 診断項目名 | 認証関連ページのno-store |
|---|---|
| 項目ID | crawl.sensitive-cache |
| 分類 | Cookie・セッション管理 |
| 重大度・配点の重み | 重要(high) 重み 3 |
| 関連する標準・ガイドライン | OWASP ASVS 5.0.0 V14.3.2(ブラウザに機微データをキャッシュさせない `Cache-Control: no-store`)、V14.2.2(サーバー側キャッシュ)、V14.2.5(キャッシュ機構の設定)/OWASP WSTG-ATHN-06(Browser Cache Weaknesses)/CWE-525(Use of Web Browser Cache Containing Sensitive Information)、CWE-524 |
認証関連ページのno-storeとは
認証関連ページのno-storeとは、ログイン画面、管理画面、マイページなど「人によって中身が変わる」「認証に関わる」ページの応答に Cache-Control: no-store を付け、どのキャッシュにも保存させないようにする対策です。
Cache-Control: no-store
no-store が無いと、ブラウザのディスクキャッシュ、戻る/進む(bfcache を含む履歴)、プロキシや CDN などの共有キャッシュに応答が残り、共用端末や後から使った別の利用者に、個人情報や管理画面の内容が見えてしまう恐れがあります。
自治体では、図書館・公民館・窓口の共用 PC、職員の共用端末、庁内プロキシなど、キャッシュが第三者に見える環境が多くあります。このため、認証系ページの no-store は基本的な対策です。セキュリティAIチェッカーは、トップページだけでなく巡回した下層ページの中から認証関連ページを見つけ、no-store の有無を確認します。
Cache-Control の主なディレクティブとの違い
| 値 | 意味 |
|---|---|
no-store | 私的・共有を問わず、いかなるキャッシュにも応答を保存しない |
no-cache | 保存はしてよいが、再利用前に必ずオリジンへ再検証する(「キャッシュしない」ではない) |
private | ブラウザ等の私的キャッシュには保存可、共有キャッシュには不可 |
max-age=0, must-revalidate | 期限切れ扱いだが保存は行われる |
名前が似ているため混同されがちですが、保存そのものを禁じるのは no-store だけです。
認証関連ページにno-storeが必要な理由
no-cache や must-revalidate では戻るボタンを防げない
MDN は、no-cache や must-revalidate は戻る/進むボタンによる履歴ナビゲーションで再検証を保証しない(bfcache が使われるとページのスナップショットが復元される)と注意しています。機微情報の保存を防ぐ目的に合うのは no-store だけです。
Chrome の bfcache と no-store
Chrome は 2025 年春以降、no-store のページでも安全と判断できる場合は bfcache に入れる(保持は最長3分)ようになりました。一方で、Cookie などの認可状態が変わると、そのページは bfcache から破棄されます。したがって、ログアウト時にセッション Cookie を確実に削除・無効化することが、戻るボタン対策の前提になります。
OWASP の考え方
- OWASP WSTG-ATHN-06(Browser Cache Weaknesses)は、機微情報を表示するページで
Cache-Control: no-storeを指示しているかを確認する手順を示しています。no-storeがあれば、must-revalidateやExpiresは現代のブラウザでは一般に不要としています。 - OWASP Session Management Cheat Sheet は、セッション ID を含む応答に
Cache-Control: no-storeを付けることを強く推奨しています。 - OWASP ASVS 5.0.0 V14.3.2 は、ブラウザに機微データをキャッシュさせないよう
Cache-Control: no-storeを設定することを求めています。
Pragma: no-cache と Expires の扱い
Pragma は HTTP/1.0 時代の要求ヘッダーで、応答での効果は規定されていません。古いプロキシ対策として併記しても害はありませんが、判定の根拠にはなりません。
認証関連ページのno-storeについてよくある誤解
- 「HTTPS ならキャッシュされない」:過去の一部ブラウザの挙動であり、現在は HTTPS の応答も通常どおりキャッシュされます。
- 「ログイン画面には個人情報がないから不要」:ログイン失敗時に入力 ID を再表示する、CSRF トークンやセッション Cookie を発行する、エラー文にアカウントの存在情報が出る、など認証画面の応答にも機微な要素が含まれます。ASVS V14.3.2 の対象は「機微データ」なので、運用上は認証画面以降を一律 no-store にするのが簡単で確実です。
- 「private を付けたから大丈夫」:private は共有キャッシュを防ぐだけで、共用 PC のブラウザキャッシュには残ります。
トップページ(入力URLの最終到達ページ)単体は「機密ページのキャッシュ制御」でも評価します。この項目は、トップページを含め、巡回で取得した最大5ページの中の認証関連ページを対象にしています。
認証関連ページのno-storeの診断基準(セキュリティAIチェッカー)
取得対象
- トップページと、同一オリジン内で巡回した最大4ページ(合計最大5ページ)。巡回時は
/login・/signin・/admin・/account・/mypage・/my-pageを含むリンクを最優先で取得します。logout/delete 等を含むパスと、画像・CSS・JS 等は巡回しません。 - 取得は GET のみです。リダイレクトは最大3回まで追跡し、最終 URL で判定します(例:
/mypage/→/login.phpに転送された場合は、最終ページを評価)。
認証関連ページの抽出条件
次の両方を満たすページを「認証関連ページ」とします。
- 最終 URL のパスに
/login・/signin・/sign-in・/admin・/account・/mypage・/my-pageがあり、その直後が/、パスの末尾、または.php・.html・.htm・.asp・.aspx・.jsp・.cgiの拡張子(login.php、mypage.html等)で終わる。トップページ単体を評価する「機密ページのキャッシュ制御」と同じ条件です。 - HTTP ステータスが 2xx(内容を返している)。404 や 403 の拒否応答は機微データを含まないため対象外です。
判定条件
| 判定 | 条件 | 得点への影響 |
|---|---|---|
| ○ 問題なし(pass) | 認証関連ページすべてで Cache-Control に no-store を含む | 重み3 × 1.00 |
| × 要改善(fail) | 1ページでも no-store が無い(no-cache・private・max-age=0 のみは不合格) | 重み3 × 0 |
| 対象外(na) | 巡回範囲に認証関連ページが無い | 計算対象外 |
- 証拠欄には「n/m ページで no-store がありません」のように件数を表示します。
- ヘッダー値の大文字・小文字は区別しません。
no-storeが他の値と併記されていても合格です。
自治体サイトでの判定例
| サイトの状態 | 判定 |
|---|---|
| 一般的な広報サイトで、ログイン系のリンクが無い | 対象外(判定可能率に影響しますが減点はしません) |
議会・入札情報の「事業者ログイン」ページ /login.php が Cache-Control: private, max-age=0 | × |
職員向け CMS 管理画面 /admin/ が同一ホストで公開され、no-store, no-cache を返す | ○(ただし管理画面の外部公開自体は別途見直し対象) |
判定の限界
URL のパターンで機微なページを推定しているため、/shinsei/kakunin のような日本語ローマ字のパスや、ログイン後にしか到達できないページは評価できません。ログイン後の画面は手動診断で確認してください。
認証関連ページにno-storeが無いとどんなリスクがあるか
- 共用端末での情報露出:公民館の PC で利用者がマイページを見た後、次の利用者が戻るボタンやキャッシュの閲覧で、前の利用者の氏名・住所・申請内容を見られる。
- 共有キャッシュへの混入:リバースプロキシや CDN が個人ページを保存し、別の利用者に配信してしまう。Web Cache Deception など、パス解釈の差を突いて個人ページをキャッシュさせる攻撃手法も知られています。
- ログアウト後の閲覧:ログアウト後に戻るボタンで管理画面が表示され、画面上の情報が第三者に見える。
- フォレンジック残存:ブラウザのキャッシュディレクトリに、個人情報を含む HTML が平文で残る。
認証関連ページにno-storeを設定する方法(Apache・PHP・nginx・IIS・Next.js)
基本方針
認証が必要なページ・認証画面・ログイン後に個人情報を返す API に Cache-Control: no-store を付与します。公開ページ(お知らせ、画像、CSS)には付けず、性能への影響を最小限にします。
Apache(.htaccess)
認証関連ディレクトリに置く .htaccess の例です。
# 認証関連ディレクトリに置く .htaccess
<IfModule mod_headers.c>
Header always set Cache-Control "no-store"
</IfModule>
ファイル名で絞る場合は次のように書きます。
<FilesMatch "^(login|mypage|account)\.php$">
Header always set Cache-Control "no-store"
</FilesMatch>
PHP
<?php
// PHP の自動キャッシュヘッダーを止め、送出する値を明示的に固定する
session_cache_limiter('');
header('Cache-Control: no-store');
session_start();
PHP の session_start() は、既定の session.cache_limiter=nocache により Cache-Control: no-store, no-cache, must-revalidate を送出します。ただし、フレームワークや設定で変更されている場合があるため、実際の応答で確認してください。
nginx
location ~ ^/(login|admin|account|mypage)(/|\.|$) {
add_header Cache-Control "no-store" always;
include /etc/nginx/snippets/security-headers.conf; # add_header 継承切れ対策
proxy_pass http://app;
proxy_no_cache 1;
proxy_cache_bypass 1;
}
location に add_header を書くと server レベルのセキュリティヘッダーが継承されなくなるため、共通ヘッダーを include で読み込み直しています。
IIS(web.config)
<location path="mypage">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="no-store" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
Next.js(App Router / Vercel)
// app/mypage/page.tsx(Cache Components を使わない従来モデル)
export const dynamic = "force-dynamic";
// またはルートハンドラで
return new Response(body, { headers: { "Cache-Control": "no-store" } });
Next.js 16 で Cache Components を有効にしている場合、dynamic の指定は使えません。next.config.js の headers() で source: "/mypage/:path*" に Cache-Control: no-store を付ける方法もあります。いずれの場合も実際の応答ヘッダーで確認し、CDN 側の設定(s-maxage 等)と矛盾しないことも確認してください。
CDN・リバースプロキシ
CDN 側で「オリジンの Cache-Control を無視して一律キャッシュ」する設定になっていないか確認します。認証系のパスはキャッシュ対象から除外するルールを明示してください。
設定後の確認方法
curl -s -o /dev/null -D - https://www.city.example.lg.jp/login.php | grep -i '^cache-control'
# 期待値: cache-control: no-store(他の値と併記でも可)
ブラウザの開発者ツールを開いた状態でログイン → マイページ表示 → ログアウト → 戻るボタン、と操作し、内容が表示されずに再読み込みやログイン画面になることも確認します。
サイトを壊さないための注意点(ガードレール)
- サイト全体に no-store を付けると、CSS・画像まで毎回取得になり表示が遅くなります。認証系のパスに限定してください。
- ログアウトの応答に
Clear-Site-Data: "cache"を付けると、そのサイトのブラウザキャッシュを消去できます(OWASP Session Management Cheat Sheet が紹介)。サイト全体のキャッシュが消えるため慎重に使い、共有キャッシュには効かない点に注意してください。 - 既に CDN のキャッシュに残っている個人ページは、設定変更後にパージ(削除)してください。
よくある質問(FAQ)
認証関連ページのno-storeの診断では、どのページが対象になりますか?
トップページと、同一オリジン内で巡回した最大4ページ(合計最大5ページ)のうち、最終URLのパスに /login・/signin・/sign-in・/admin・/account・/mypage・/my-page があり、HTTPステータスが2xxのページです。巡回時はこれらを含むリンクを最優先で取得します。404や403の拒否応答は機微データを含まないため対象外です。
認証関連ページに Cache-Control: private を付けていれば十分ですか?
十分ではありません。private は共有キャッシュ(プロキシやCDN)への保存を防ぐだけで、ブラウザ等の私的キャッシュには保存されます。共用PCで次の利用者に見られるリスクは残るため、no-store が必要です。
HTTPSのページはキャッシュされないのではありませんか?
それは過去の一部ブラウザの挙動です。現在は HTTPS の応答も通常どおりキャッシュされるため、HTTPSであっても no-store の指定が必要です。
ログイン画面には個人情報がないので no-store は不要ですか?
ログイン失敗時に入力IDを再表示する、CSRFトークンやセッションCookieを発行する、エラー文にアカウントの存在情報が出る、など認証画面の応答にも機微な要素が含まれます。運用上は、認証画面以降を一律 no-store にするのが簡単で確実です。
ログアウト後に戻るボタンで画面が表示されないようにするには?
認証系ページに no-store を付けたうえで、ログアウト時にセッションCookieを確実に削除・無効化します。Chromeは条件によって no-store のページも最長3分 bfcache に保持しますが、Cookie などの認可状態が変わるとそのページを破棄します。
Pragma: no-cache を付ければ代わりになりますか?
代わりになりません。Pragma は HTTP/1.0 時代の要求ヘッダーで、応答での効果は規定されていません。古いプロキシ対策として併記しても害はありませんが、診断の判定根拠にはなりません。
認証関連ページが見つからない場合、no-storeの診断ではどうなりますか?
巡回範囲に /login や /mypage などの認証関連ページが無ければ「対象外」になり、減点はしません。ログイン後にしか到達できないページは外部から評価できないため、手動で確認してください。
この項目を含む127項目を、URLを入れるだけで無料診断します。