結論(危険なCORSワイルドカードがない)
危険なCORSワイルドカードがないとは、サーバーが応答ヘッダーAccess-Control-Allow-Originに、全サイトからの読み取りを許す「*」や、事実上誰でも許可と同じ働きをする「null」を返していない状態です。* はオープンデータなど公開情報には正当ですが、庁内限定情報やURLにトークンを含むAPIでは外部サイトから応答を読まれる原因になります。セキュリティAIチェッカーは、トップページの応答が * または null なら △ 要確認と判定します。
この記事のポイント
- CORSは同一オリジンポリシーをサーバーの許可で緩める仕組みで、Access-Control-Allow-Originがその許可範囲を示します。
- * はCookie等の認証情報を伴わない要求にだけ有効ですが、IPやネットワーク位置で保護された情報、URL内トークンのAPIでは危険です。
- null はサンドボックス化したiframe等で誰でも作れるオリジンのため、許可してはいけません。
- 診断はトップページの応答が * または null なら △(重み3×0.45=1.35点)、ヘッダーなしか具体的なオリジンなら ○ です。
- 対策は、CORSを公開データ用パスだけに限定し、機密APIでは許可リストと照合して一致したオリジンだけを返すことです。
| 診断項目名 | 危険なCORSワイルドカードがない |
|---|---|
| 項目ID | headers.cors |
| 分類 | CORS(クロスオリジン設定) |
| 重大度・配点の重み | 重要(high) 重み 3 |
| 関連する標準・ガイドライン | OWASP ASVS 5.0.0 V3.4.2(Access-Control-Allow-Originは固定値か許可リストで検証した値とし、* を使う場合は応答に機密情報を含めない、L1)、OWASP WSTG-CLNT-07(Cross Origin Resource Sharing)、WHATWG Fetch Standard(CORS protocol / CORS check)、CWE-942(信頼できないドメインを許可する過度に緩いクロスドメインポリシー) |
危険なCORSワイルドカードがないとは(Access-Control-Allow-Originの確認)
危険なCORSワイルドカードがないとは、サーバーが応答ヘッダー Access-Control-Allow-Origin に、全サイトからの読み取りを許す *(ワイルドカード)や、同等に危険な null を返していない状態のことです。セキュリティAIチェッカーはこの項目(headers.cors)で、トップページの応答にこのワイルドカード、または null の許可が含まれていないかを確認します。
ブラウザには「同一オリジンポリシー」があり、あるサイト(例:https://evil.example)のJavaScriptが、別のサイト(例:https://www.city.example.lg.jp)の応答内容を読み取ることを原則として禁止しています。CORS(Cross-Origin Resource Sharing)は、この制限をサーバー側の許可によって緩める仕組みです。
サーバーが応答に Access-Control-Allow-Origin: * を付けると、「どのサイトのJavaScriptからでも、この応答を読んでよい」という意味になります。
| 項目 | 内容 |
|---|---|
| 項目ID | headers.cors |
| 診断カテゴリ | API・通信設定 |
| 重大度 | high |
| 重み | 3 |
| 関連標準 | OWASP ASVS 5.0.0 V3.4.2(L1)、OWASP WSTG-CLNT-07、WHATWG Fetch Standard(CORS protocol / CORS check)、CWE-942 |
| 関連項目 | 「CORSの認証情報設定に矛盾がない」、「任意Originを反射しない」、「動的CORSでVary: Originを返す」、「CORSプリフライトの許可範囲」 |
Access-Control-Allow-Originの値と評価
| 値 | 意味 | 評価 |
|---|---|---|
| ヘッダーなし | クロスオリジンでの読み取りを許可しない(ブラウザの既定動作) | 安全 |
https://app.example.jp | 指定した1つのオリジンにだけ読み取りを許可 | 許可リストに基づくなら適切 |
* | 全オリジンに読み取りを許可(Cookie等の認証情報を伴わない要求に限る) | 公開データ専用なら可。機密情報を含む応答では不可 |
null | オリジンが null と直列化される文書に許可 | 使用すべきでない |
CORSワイルドカード「*」はなぜ危険になり得るのか(Fetch StandardのCORS check)
ブラウザはFetch Standardの手順で応答を読めるかどうかを判定します。要点は次のとおりです。
Access-Control-Allow-Originがなければ失敗(読めない)。- 要求が認証情報を伴わず(credentials modeが
includeでない)、値が*なら成功。 - それ以外は、値が要求元オリジンの文字列と完全一致しなければ失敗。
- 認証情報を伴う要求では、さらに
Access-Control-Allow-Credentials: trueが必要。
つまり * は「Cookieを付けない要求」に対してだけ有効で、Cookieを付けた要求では * は通りません。このため「* でもログイン情報は盗まれない」と言われますが、次の点に注意が必要です。
- IPアドレスやネットワークの位置で保護された情報:WSTG-CLNT-07は、
*は認証情報と併用できないものの、アクセス制御をファイアウォールや送信元IPだけで行っている場合には危険になり得ると指摘しています。庁内ネットワークからだけ見える情報を、職員のブラウザ経由で外部サイトに読まれる構図です。 - URLに秘密を含むAPI:トークンをクエリ文字列で渡す設計では、Cookieがなくても機密データが読めます。
なお、Chromeは142以降、公開サイトからローカルネットワーク(プライベートIPアドレスやループバック)への要求に利用者の許可を求める「Local Network Access」を導入しています。ただし、利用者が許可した場合や他のブラウザでは防げないため、サーバー側で * を外すことが必要です。
nullオリジンを許可してはいけない理由
MDNは、data: や file: のような非階層スキームの文書や、サンドボックス化された文書のオリジンは null と直列化され、多くのブラウザがそうした文書に Access-Control-Allow-Origin: null の応答へのアクセスを許すため、null は使うべきでないと明記しています。
攻撃者は <iframe sandbox="allow-scripts"> で自分のページを null オリジンにできるため、null の許可は事実上「誰でも許可」と同じ働きをします。WSTG-CLNT-07も、null オリジンは悪用可能な「ワイルドカード」として機能すると説明しています。
CORSについてよくある誤解
- 「CORSを設定すればCSRFも防げる」:CORSは「応答を読めるか」の制御であり、要求の送信自体は止めません。状態を変える処理にはCSRFトークンやSameSite Cookieが別途必要です。
- 「
*はすべて危険」:オープンデータAPIや、誰でも取得してよい公開ファイル(地図タイル、公開CSV等)では、*は正当で推奨される設定です。ASVS V3.4.2も、*を使う場合は応答に機密情報を含めないことを求めているだけで、使用自体を禁じてはいません。
危険なCORSワイルドカードがないの診断基準(セキュリティAIチェッカー)
取得対象
- 入力URLへのGET(
Originヘッダーは付けない、リダイレクト最大4回追跡)で得た最終ページの応答ヘッダーAccess-Control-Allow-Origin。 Originを付けた要求で反射されるかどうか(動的CORS)は、関連項目「任意Originを反射しない」が同一オリジンのAPI候補に対して検査します。
判定条件
| 判定 | 条件 | 得点への影響 |
|---|---|---|
| ○ 問題なし(pass) | ヘッダーがない、または具体的なオリジンを指定 | 重み3を満点で加算 |
| △ 要確認(warn) | 値が * または null(前後の空白を除き、大文字小文字を区別しない) | 重み3×0.45=1.35点 |
- 根拠欄にはヘッダー値(ない場合は「トップページではCORS許可ヘッダーなし。」)を表示します。
- この項目には × 要改善(fail)や対象外(na)の判定はありません。
- × ではなく △ としているのは、
*が公開データに対しては正当な設定であり、応答に機密情報が含まれるかどうかは外部からの観測だけでは判断できないためです。 - トップページという公開ページで
*が付いている場合、多くはWebサーバー全体に一括設定された結果です。同じ設定がAPIやログイン後画面にも及んでいないか確認を促す意味があります。
* や null と Access-Control-Allow-Credentials: true の組み合わせ(矛盾した設定)は「CORSの認証情報設定に矛盾がない」、要求の Origin をそのまま返す動的CORSは「任意Originを反射しない」で評価します。このページの項目は、トップページで観測できる固定のワイルドカード設定に集中しています。
自治体サイトでの判定例
| 状況 | 判定 |
|---|---|
| 一般的な自治体公式サイト:トップページにCORSヘッダーなし | ○ |
CDNやクラウドストレージで静的配信し、全ファイルに Access-Control-Allow-Origin: * を付与 | △(公開情報のみなら許容可能。申請・照会系の配信元が同じ設定でないか確認) |
開発時の設定が残り Access-Control-Allow-Origin: null | △(削除すべき設定) |
CORSワイルドカードがあるとどんなリスクがあるか
- 庁内限定情報の読み取り:庁内ネットワークのみ公開のシステムが
*を返している場合、職員が外部の悪性サイトを閲覧しただけで、そのページのJavaScriptが職員のブラウザを踏み台にして庁内システムの応答を読み取り、外部へ送信できます。 - URL内トークンによる情報取得:トークン付きURLで個人データを返すAPIが
*を返すと、トークンを知った第三者のサイトから自由に読み取られます。 null許可+認証情報:nullとAccess-Control-Allow-Credentials: trueを組み合わせると、ログイン中の利用者の個人データが外部サイトから読み取られる、実際に悪用可能な構成になります(「CORSの認証情報設定に矛盾がない」で評価)。
CORSの正しい設定方法(Apache・nginx・IIS・Next.js)
基本方針
- CORSが本当に必要なパスを洗い出す(多くの自治体サイトのHTMLページでは不要)。
- 公開データ配信用のパスだけに限定して付与する。
- 機密データを扱うAPIでは、許可オリジンのリストと照合し、一致したときだけそのオリジンを返す。
nullは許可しない。
Apache(.htaccess):公開データのディレクトリだけに付与
# /opendata/.htaccess(オープンデータCSVなど公開情報専用)
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
サイトのルートやサーバー全体の設定に Header set Access-Control-Allow-Origin "*"(または Header always set)が書かれている場合は削除してください。
Apache:許可リスト方式
<IfModule mod_headers.c>
SetEnvIf Origin "^(https://(?:www|map)\.city\.example\.lg\.jp)$" CORS_ALLOW=$1
Header always set Access-Control-Allow-Origin "%{CORS_ALLOW}e" env=CORS_ALLOW
Header always merge Vary "Origin"
</IfModule>
nginx
# mapはhttpコンテキストに記述
map $http_origin $cors_allow {
default "";
"https://www.city.example.lg.jp" $http_origin;
"https://map.city.example.lg.jp" $http_origin;
}
server {
location /api/ {
add_header Access-Control-Allow-Origin $cors_allow always;
add_header Vary Origin always;
}
}
$cors_allow が空文字のとき、nginxはヘッダーを出力しません。なお、location 内に add_header を書くと、server レベルの add_header(他のセキュリティヘッダーを含む)はその location に継承されないため、必要なヘッダーは同じ location にも記述してください。
IIS(web.config)
<system.webServer>
<httpProtocol>
<customHeaders>
<remove name="Access-Control-Allow-Origin" />
</customHeaders>
</httpProtocol>
</system.webServer>
まず不要なワイルドカードを除去し、必要なアプリケーションだけでIIS CORS Moduleの <cors> 設定により許可オリジンを列挙します。
Next.js(App Router)
// app/api/data/route.ts
const ALLOWED = new Set(["https://www.city.example.lg.jp"]);
export async function GET(req: Request) {
const origin = req.headers.get("origin") ?? "";
const headers = new Headers({ "Content-Type": "application/json", Vary: "Origin" });
if (ALLOWED.has(origin)) headers.set("Access-Control-Allow-Origin", origin);
return new Response(JSON.stringify({ ok: true }), { headers });
}
確認方法(curl)
curl -sI https://www.city.example.lg.jp/ | grep -i '^access-control-'
curl -sI -H 'Origin: https://evil.example' https://www.city.example.lg.jp/api/xxx | grep -i '^access-control-'
curl -sI -H 'Origin: null' https://www.city.example.lg.jp/api/xxx | grep -i '^access-control-'
2行目・3行目で evil.example や null がそのまま返る場合は要修正です。設定変更後はセキュリティAIチェッカーで再診断し、本項目と関連するCORSの各項目を確認します。
サイトを壊さないための注意点(ガードレール)
- 地図、オープンデータ、外部ポータルへ提供しているJSON等で
*を消すと、他サイトからの利用が止まります。提供先を確認し、公開用パスに限定して残してください。 - 許可リスト方式では、動的に値が変わるため
Vary: Originを必ず付け、CDNのキャッシュ混同を防いでください(「動的CORSでVary: Originを返す」で確認します)。
よくある質問(FAQ)
Access-Control-Allow-Origin: * はすべて危険ですか?
すべてではありません。オープンデータAPIや、地図タイル・公開CSVなど誰でも取得してよい公開ファイルでは、* は正当で推奨される設定です。ASVS V3.4.2も、* を使う場合は応答に機密情報を含めないことを求めているだけで、使用自体は禁じていません。
* ならCookieが送られないのでログイン情報は盗まれないのでは?
Cookieを付けた要求では * は通らないのは事実です。しかし、ファイアウォールや送信元IPだけで保護された庁内向け情報は、職員のブラウザを踏み台に外部サイトから読まれる可能性があります。トークンをクエリ文字列で渡すAPIも、Cookieなしで機密データが読めてしまいます。
Access-Control-Allow-Origin: null はなぜ危険なのですか?
data: や file: の文書、サンドボックス化された文書のオリジンは null と直列化され、多くのブラウザがそうした文書に null 許可の応答へのアクセスを許すためです。攻撃者は sandbox 付きのiframeで自分のページを null オリジンにできるので、null 許可は事実上「誰でも許可」と同じです。
CORSを正しく設定すればCSRFも防げますか?
防げません。CORSは「応答を読めるか」の制御で、要求の送信自体は止めません。状態を変える処理には、CSRFトークンやSameSite Cookieが別途必要です。
CORSワイルドカードの診断で △ になりましたが、トップページに * があるのはなぜですか?
多くはWebサーバーやCDN全体に一括設定された結果です。公開情報だけなら許容できますが、同じ設定がAPIやログイン後画面、申請・照会系の配信元にも及んでいないかを確認してください。
許可リスト方式で注意することは?
要求のOriginに応じて返す値が変わるため、Vary: Origin を必ず付けてCDNのキャッシュ混同を防ぎます。また、* を外すと地図やオープンデータを利用している他サイトが止まるため、提供先を確認して公開用パスに限定して残します。
この項目を含む127項目を、URLを入れるだけで無料診断します。