• 診断項目 No.101/127
  • 認証・アクセス制御
  • 重大度 最重要(critical)

Cookie・Referer偽装でアクセス範囲が増えないとは?認証回避の仕組み・正しい実装・診断基準

Cookie・Referer偽装でアクセス範囲が増えないとは、任意のCookieや自サイトのRefererを付けても保護ページが見られない状態です。偽装で突破される実装、サーバー側セッションでの正しい認可、4条件比較の診断基準を解説します。

結論(Cookie・Referer偽装でアクセス範囲が増えない)

Cookie・Referer偽装でアクセス範囲が増えないとは、保護されたページに任意のセッションID風のCookieや自サイトを示すRefererを付けて要求しても、何も付けない場合と同じ拒否応答が返り、見られる範囲が広がらない状態です。RefererやCookieの「存在」はcurlなどで自由に作れるため、認証の根拠にはなりません。ログイン済みかどうかは、サーバーが発行したセッションIDをサーバー側のセッションストアで確認して判断します。

この記事のポイント

  • Referer やCookieの有無・値は、ブラウザ以外のツールで誰でも自由に作れます。
  • 「ログイン画面から来た人だけ表示」「Cookieが付いていれば会員扱い」という実装は、ヘッダーを1つ付けるだけで突破されます。
  • セキュリティAIチェッカーは保護されていそうなURL1件に、何も付けない・任意Cookie・Referer・両方の4条件でGETし、応答を比べます。
  • 何も付けない要求が拒否なのに、偽装した条件で2xxになれば×(要改善)です。
  • 正しい実装では、存在しないセッションIDを送っても未ログイン扱いのままなので、4条件の応答はすべて同じ拒否になります。
診断項目名Cookie・Referer偽装でアクセス範囲が増えない
項目IDauth.unauthenticated-variant-diff
分類認証・アクセス制御
重大度・配点の重み最重要(critical) 重み 5
関連する標準・ガイドラインOWASP ASVS 5.0.0 V7.2.1(セッショントークンの検証は信頼できるバックエンドで行う)、V8.3.1(認可は信頼できるサービス層で行い、利用者が操作できる制御に依存しない)、V8.2.1/OWASP WSTG-ATHN-04(Bypassing Authentication Schema)、WSTG-ATHZ-02(Bypassing Authorization Schema)/CWE-293(Using Referer Field for Authentication)、CWE-302(Authentication Bypass by Assumed-Immutable Data)、CWE-565(Reliance on Cookies without Validation and Integrity Checking)/IPA「安全なウェブサイトの作り方」アクセス制御や認可制御の欠落

Cookie・Referer偽装でアクセス範囲が増えないとは

Cookie・Referer偽装でアクセス範囲が増えないとは、会員ページや管理画面など保護されたページに対して、任意のセッション ID 風の Cookie や、自サイトを示す Referer ヘッダーを付けて要求しても、何も付けない場合と同じ拒否応答が返り、見られる範囲が広がらない状態のことです。

「ログイン画面から来た人だけ見られる」「Cookie が付いていれば会員扱い」といった、偽装できる情報でアクセスを許す実装は、自治体の委託開発や簡易な会員ページで今も見られます。Referer ヘッダーや Cookie の 存在 は、ブラウザ以外のツールで自由に作れるため、認証の根拠になりません。

セキュリティAIチェッカーのこの項目は、保護されていそうな URL に対して、次の4条件で要求を送り、偽装した条件でだけ応答が「拒否」から「成功」に変わらないかを比較します。

  1. 何も付けない
  2. 任意のセッション ID 風の Cookie を付ける
  3. 自サイトを示す Referer を付ける
  4. 2 と 3 の両方を付ける

HTML の中のスクリプトから兆候を探す「Refererをアクセス制御に使っていない」や「クライアント保存値を認証判定に使っていない」と違い、この項目は実際に要求を送って、サーバーの応答が偽装で変わるかを確かめます。

なぜCookieやRefererの偽装で認証が突破されるのか

偽装できる要素と典型的な誤実装

要素偽装方法典型的な誤実装
Referercurl の -e、プロキシで書き換えif (strpos($_SERVER['HTTP_REFERER'], '/login.php') !== false) で表示許可
Cookie の存在-H "Cookie: ..." で任意の値if (isset($_COOKIE['login'])) や login=1 で会員扱い
セッション ID の形式任意の文字列ID の形式だけ確認し、サーバー側のセッション状態を見ない
クライアント側 JS無効化・直接 URL 入力sessionStorage の値で画面遷移を制御

標準・ガイドラインの考え方

  • OWASP ASVS 5.0.0 V8.3.1 は、認可を信頼できるサービス層で行い、信頼できない利用者が操作できる制御に依存しないことを要求しています。
  • ASVS 5.0.0 V7.2.1 は、セッショントークンの検証を信頼できるバックエンドで行うことを求めています。
  • CWE-293 は、Referer を認証に使う弱点として、Referer が容易に偽装できることを説明しています。
  • OWASP WSTG-ATHN-04 も、固定値のパラメーター(Cookie に保存されたものを含む)でログイン成功を判断する設計を、値の書き換えで保護領域に入れる認証回避の手口として挙げています。

正しい実装との違い

正しい実装では、サーバーが発行した推測困難なセッション ID を受け取り、サーバー側のセッションストアで「ログイン済みか」「誰か」「権限は何か」を確認します。存在しない(任意の)セッション ID を送っても未ログイン扱いのままなので、4条件の応答はすべて同じ拒否(ログイン画面へのリダイレクトや 401/403)になります。

Cookie・Referer偽装についてよくある誤解

  • 「Referer は改ざんできない」:ブラウザの通常操作では変えにくいだけで、HTTP クライアントなら自由に設定できます。逆に、プライバシー設定や Referrer-Policy によって送られないこともあり、正規の利用者が弾かれる原因にもなります。
  • 「Cookie は HttpOnly だから改ざんされない」:HttpOnly は JavaScript から読めなくするだけで、利用者自身(攻撃者自身)が送る Cookie の値は自由に作れます。
  • 「URL を知らなければアクセスされない」:URL は履歴・Referer・ログ・検索エンジン経由で漏れます。

Cookie・Referer偽装でアクセス範囲が増えないの診断基準(セキュリティAIチェッカー)

比較対象 URL の選び方

トップページと追加取得した最大2ページの HTML から抽出した同一オリジンの URL(リンク・フォームの action・スクリプト中の遷移先など)と、巡回済みページ・API 候補の URL を材料にし、次の条件に合う最初の1件だけを比較します。

  1. まず「認証後静的ファイル候補」を優先します。これは「認証後静的ファイルの未認証到達性」と同じ候補で、パスワード欄のあるページから location.href 等で遷移する先やパスワードフォームの送信先になっている .html/.htm/.txt/.csv、または会員・マイページ・管理系パス配下の静的ファイルです。パスワード欄やクライアント側認証の兆候があるページが1つも無ければ、候補はありません。
  2. 1 が無ければ、URL のパス部分に /admin・/account・/mypage・/my-page・/member・/members・/dashboard・/private・/secure を含み、その直後が /・.・末尾のものを使います(ホスト名に同じ語があっても対象にしません)。
  3. 候補が無ければ対象外(na)とします。

送信する4条件

すべて GET(タイムアウト5秒)で送り、リダイレクトは追跡せず、最初の応答を比較します。本文が 100KB を超える応答は取得失敗として扱います。

条件付与するヘッダー
基準なし
CookiePHPSESSID=<ランダム値>; JSESSIONID=<ランダム値>
RefererReferer: <対象オリジン>/
両方上記の Cookie と Referer

判定条件

判定条件得点への影響
× 要改善(fail)基準が 2xx 以外(拒否・リダイレクト)なのに、Cookie/Referer を付けた条件のいずれかが 2xx重み5 × 0
△ 要確認(warn)基準からすでに 2xx(公開ページか、認証が無いかを要確認)、または4条件で「ステータス+Location ヘッダー+本文ハッシュ」に差がある重み5 × 0.45
○ 問題なし(pass)4条件すべてが同一のステータス・Location・本文(nonce-… の値は正規化)で、基準が 2xx 以外(全条件が 404/410 の場合を除く)重み5 × 1.00
対象外(na)候補 URL が無い、4条件のいずれかの取得に失敗、または4条件すべてが 404/410(候補 URL が存在せず比較できない)計算対象外
  • 本文ハッシュは、先頭 100,000 文字の SHA-256 です。
  • 比較するのはステータス・Location ヘッダーの値・本文の3つで、条件によって転送先だけが変わる場合も差として扱います。それ以外のヘッダーの差は判定に使いません。
  • CSRF トークンや時刻を本文に埋め込むページ、転送先 URL に毎回異なる値を付けるページは、条件ごとに差が出て △ になることがあります。その場合は、内容が「同じ拒否画面」かを手動で確認してください。
  • 4条件すべてが 404 または 410 のときは、証拠欄に「保護URL候補が存在しないため比較できませんでした」と表示して対象外とします。

自治体サイトでの判定例

サイトの状態判定
会員ページの候補が無い一般的な広報サイト対象外
/member/index.html が基準で 302(ログイン画面へ)、Referer 付きで 200×(Referer によるアクセス制御)
/mypage/ が4条件とも 302 で、転送先(Location)も本文も同じ○
スクリプトに残っていた /member/old.html が4条件とも 404対象外
/admin/ が基準で 200 の公開説明ページ△(公開してよい内容か確認)

Cookie・Referer偽装で突破されるとどんなリスクがあるか

  • 認証回避による情報閲覧:攻撃者が curl -e https://example/login.php https://example/member/list.html のように Referer を付けるだけで、会員限定の名簿・申請情報・内部資料を取得できます。
  • 権限昇格:Cookie の role=admin や login=1 の存在で管理機能を表示する実装では、Cookie を作るだけで管理画面に入れます。
  • 自動収集:一度手口が分かると、スクリプトで全ページを機械的に取得されます。アクセスログ上は通常の GET と区別しにくいのも問題です。
  • ASVS や CWE が典型的な弱点として挙げている型であり、IPA の「安全なウェブサイトの作り方」でも、アクセス制御や認可制御の欠落は重要な脆弱性として扱われています。

Cookie・Referer偽装で突破されない認証の実装方法(PHP・Apache・nginx・IIS・Next.js)

基本方針

  1. 認証済みかどうかは サーバー側セッション だけで判断する。
  2. Referer・Cookie の有無・クライアント JS の状態を、認可の根拠にしない。
  3. 保護ページは静的ファイルとして直接配信せず、認証チェック付きのプログラム経由で返す。

PHP(正しい例)

<?php
session_start();
if (empty($_SESSION['user_id'])) {          // サーバー側に保存した値だけを見る
    header('Location: /login.php', true, 302);
    exit;
}
// 権限チェック
if (!in_array('member', $_SESSION['roles'] ?? [], true)) {
    http_response_code(403);
    exit;
}

ログイン成功時に session_regenerate_id(true) を呼び、セッション固定化も防ぎます。session.use_strict_mode=1 にすると、サーバーが発行していない ID は受け付けられません。

Apache(静的ディレクトリの保護)

アプリに移行するまでの暫定策として、静的な会員ディレクトリを Web サーバーの認証で保護します。

# /member/.htaccess
AuthType Basic
AuthName "Members"
AuthUserFile /path/outside/docroot/.htpasswd
Require valid-user

SetEnvIf Referer で許可する方式は、Referer の偽装で突破されるため使いません。

nginx

location /member/ {
    auth_request /auth/check;   # アプリ側でセッションを検証し 2xx/401 を返す
    error_page 401 = @login;
}
location = /auth/check {
    internal;
    proxy_pass http://app/session/verify;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}
location @login { return 302 /login; }

IIS

Windows 認証やフォーム認証を使い、system.webServer の security → authorization に <add accessType="Deny" users="?" /> を置いて未認証を拒否します(IIS の URL 承認機能。静的ファイルにも適用されます)。ASP.NET の system.web の authorization は、既定ではマネージドハンドラーの要求にしか適用されない点に注意してください。

Next.js

// middleware(proxy)で Cookie の存在だけを見るのは不十分。
// セッション ID をサーバー側ストア/署名付きトークン検証で確認する。
export async function GET(req: Request) {
  const session = await getServerSession(req);   // 検証済みセッションを取得
  if (!session) return new Response(null, { status: 401 });
  return Response.json(await loadMemberData(session.userId));
}

設定後の確認方法

U=https://www.city.example.lg.jp/member/index.html
curl -s -o /dev/null -w "%{http_code}\n" "$U"
curl -s -o /dev/null -w "%{http_code}\n" -H "Cookie: PHPSESSID=test123" "$U"
curl -s -o /dev/null -w "%{http_code}\n" -e "https://www.city.example.lg.jp/login.php" "$U"

すべて同じ拒否コード(302/401/403)なら正常です。トップページだけでなく、ログイン画面の URL を Referer に入れる確認も行ってください。

サイトを壊さないための注意点(ガードレール)

  • 認証方式を変えると、既存の会員がログインし直しになるため、切替日を告知してください。
  • 旧静的 URL がブックマークされている場合、保護後は 404 またはログイン画面へ転送し、内容を返さないようにします。
  • 診断ツールは、公開情報から推定した1 URL だけを比較します。会員ページ・管理画面の全 URL について、認証の有無・権限ごとのアクセス可否表(認証マトリクス)を作成し、手動で確認してください。

よくある質問(FAQ)

Refererでアクセス制御するのはなぜ危険なのですか?

Referer はブラウザの通常操作では変えにくいだけで、curl の -e オプションやプロキシでの書き換えにより自由に設定できます。逆にプライバシー設定や Referrer-Policy で送られないこともあり、正規の利用者が弾かれる原因にもなります。

HttpOnlyのCookieなら改ざんされないのではありませんか?

HttpOnly は JavaScript から Cookie を読めなくするだけです。利用者自身(攻撃者自身)が送る Cookie の値は自由に作れるため、Cookie の存在や login=1 のような値で会員扱いする実装は突破されます。

URLを公開していなければアクセスされませんか?

URL はブラウザ履歴、Referer、アクセスログ、検索エンジン経由で漏れます。URLを知られないことは保護になりません。

Cookie・Referer偽装でアクセス範囲が増えないかの診断で、△(要確認)になるのはどんな場合ですか?

何も付けない要求でもすでに2xxが返る場合(公開ページか、認証が無いかの確認が必要)と、4条件で「ステータス・Locationヘッダー・本文」に差がある場合です。CSRFトークンや時刻を本文に埋め込むページでも差が出るため、内容が同じ拒否画面かを手動で確認してください。

正しい実装はどのようなものですか?

サーバーが発行した推測困難なセッションIDを受け取り、サーバー側のセッションストアで「ログイン済みか」「誰か」「権限は何か」を確認します。PHPなら $_SESSION の値だけを見て、ログイン時に session_regenerate_id(true) を呼び、session.use_strict_mode=1 でサーバーが発行していないIDを受け付けないようにします。

Cookie・Referer偽装の診断で問題なしなら、会員ページ全体が安全ですか?

安全とは限りません。診断ツールは公開情報から推定した1つのURLだけを比較します。会員ページ・管理画面の全URLについて、認証の有無・権限ごとのアクセス可否表(認証マトリクス)を作成し、手動で確認してください。

セキュリティAIチェッカーを利用する

この項目を含む127項目を、URLを入れるだけで無料診断します。

参考資料