The AI Summary Said "It's Not a Scam." The Springboard Was Your Site's Search Boxのカバー画像

Ken Imoto

昨年8月、クルーズ旅行を計画していた男性がRoyal Caribbeanのカスタマーサービス電話番号をGoogleで検索した。GoogleのAI Overviewが検索結果の上部に電話番号を表示した。男性はその番号に電話し、カード情報を渡したが、その番号は詐欺師のものだった。同様の事例がSouthwest Airlinesの検索でも発生した。この亜種——AIサマリーが拾う場所に偽のサポート番号を仕込む——は十分に報道された。

先週、日本の警視庁がより静かな亜種を発表した。開発者にとってより注意すべき点がある。攻撃対象は正規サイトにある:検索ボックスだ。もしかするとあなたのサイトにもあるかもしれない。

警察が説明した状況はこうだ:誰かがSNS上の投資グループに招待される。お金を送る前に、念のためグループ名を検索する。結果には「XXは詐欺ではありません」「XXで稼げました」と表示される。ページ上部のAIサマリーも「XXは詐欺ではありません」と同意する。安心して送金する。

被害者の検証習慣——「信用する前に検索しよう」——が罠に組み込まれていた。

報告を読んで、最初に疑問に思ったのは「どうやって?」だった。私は日常的にLLMO(AI検索で引用されるためのサイト最適化)に取り組んでいるので、SEO界隈で囁かれる検索汚染手法のどれかだろうと疑った。たどり着いたのは、2023年2月に日本のSEO企業JADEが文書化したものだった。予想より古く、単純だった:サイト検索スパム。

この記事では仕組み、AIサマリーが嘘を繰り返す理由、そして今週中に実装できる防御策(noindex、X-Robots-Tag、ヒットゼロ時の404)について説明する。

仕組み:3ステップ、ハッキング不要

検索ボックスを持つほとんどのサイトは、/search?q=keywordのようなURLで結果を返す。典型的な実装の2つの特性が攻撃を可能にする:

  • 誰でもクエリパラメータに任意の文字列を入れられる
  • ページがそのクエリを<title><h1>に反映する(「'keyword'の検索結果 | Acme Corp」)

攻撃の手順:

  1. 攻撃者が信頼できるドメイン上に検索URLを作成する:acme.com/search?q=XX+is+not+a+scam。検索ボックスに触れる必要はない。URLだけで十分だ。
  2. 自らが管理するサイトからそのURLへリンクする。
  3. Googlebotがリンクを辿り、結果ページをクロールしてインデックスする。以降、ウェブ検索で「XX is not a scam | Acme Corp」が正規ドメインの下に表示されるようになる。

被害を受けたサイトは侵害されたわけではない。マルウェアも侵入もツールも不要。攻撃者はURLを1つ作り、リンクを1つ置いただけだ。初めて理解したとき、私は思わず「え、それだけ?」と声に出した。悪用されているのは脆弱性ではなく、仕様だ。

検索する人には、Acme Corpのサイトが「詐欺ではない」と言っているように見える。長年かけて築いたドメインの信頼が、他人の文章に貸し出される。

AIサマリーが嘘を繰り返す理由

AI Overviewなどの機能はRAGに構造的に近い:検索インデックスからクエリに関連するページを取得し、そこから回答を生成する。内部は公開されていないが、依存関係は観測可能だ。サマリーはインデックスの下流で作られる。

AIには仕掛けを嗅ぎ分ける方法がない。取得したのは、AIから見れば信頼できるドメイン上のテキストだ。主張の真偽を検証せず、ソースの権威性と複数ソース間の一致度を重視する。したがって、攻撃者が同じ文章を複数の信頼できるドメインの検索URLに仕込めば、AIは複数の独立した権威あるソースが一致していると判断する。

ここが厄介なところだ。AIが権威シグナルを真剣に重み付けするほど、この攻撃は効きやすくなる。真面目なAIほど、だまされやすい。

パイプラインは単純だ。上流に検索インデックス、下流にAIサマリー。上流を汚染すれば下流も自ら汚染される。AIベンダーがより良いフィルタを実装するのを待つか、自サイトの反映面を閉じるか。前者は待たされるが、後者は速く、しかも自らの制御下にある。

5分でできるセルフチェック

あなたのサイトが踏み台にされる可能性はあるか。3つの確認:

# 1. 検索結果ページはインデックスされているか?(Googleで)
site:example.com inurl:search
site:example.com inurl:"?s="

# 2. 怪しいフレーズでインデックスされているか?
site:example.com scam
site:example.com refund

Enter fullscreen mode Exit fullscreen mode

# 3. 検索結果ページにnoindexが付いているか?
curl -sI "https://example.com/search?q=test" | grep -i x-robots-tag

# ヘッダーがない場合、HTMLのmetaタグを確認
curl -s "https://example.com/search?q=test" | grep -i '<meta name="robots"'

Enter fullscreen mode Exit fullscreen mode

site:クエリは簡易的なスモークテストだ。Googleは網羅的な結果を保証していない。確定的な回答を得るには、Search Consoleを開いて「インデックス登録」>「ページ」と「パフォーマンス」>「ページ」で、/search?s=を含むURLを確認する。

また、検索結果テンプレートを確認しよう。クエリを<title><h1>に反映しているか?反映とインデックス許可の組み合わせが、標的になる条件だ。

安心材料:クライアントサイド検索(静的サイトでよく見られる、ブラウザでのJSフィルタリング)はこの攻撃面を持たない。サーバーがクエリごとに異なるHTMLを返さないためだ。

防御策

JADEの推奨に基づく2つの有効な戦略:

対策 効果 注意点
<meta name="robots" content="noindex"> 結果ページを確実にインデックスから除外 robots.txtでページをブロックすると無効化される
X-Robots-Tag: noindexヘッダー テンプレートを触らずインフラレベルで適用 同上
ヒットゼロのクエリにnoindex(または404) 検索ページのSEOトラフィックを維持しつつスパムをブロック 404は正当なヒットゼロクエリでのUXを損なう可能性
robots.txtDisallow: /search クロールを抑制 単独では不十分——外部リンク経由でブロックURLがインデックスされる可能性

選び方は単純だ:

  • 検索結果ページでSEOトラフィックを狙っていないなら、すべてnoindex。最速で最も確実。
  • そのトラフィックを維持したいなら、ヒットゼロのクエリにnoindexを返す。「XXは詐欺ではありません」などのスパムフレーズはほぼ常にヒットゼロなので、これだけで攻撃の大部分を防げる。

注意すべき落とし穴がある:noindexはクローラーがページを読み込める場合にのみ有効だ。robots.txtでURLをブロックすると、クローラーはnoindexを見ないため、防御全体が無効化される。Googleのドキュメントにも明記されている。noindexを有効にするには、ページがrobots.txtでブロックされていない必要がある。同じURLに両方を適用してはいけない。

実装例。

WordPressの検索ページ(?s=)は、Yoastなどを利用していればデフォルトでnoindexになる。素のテーマでは、wp_robotsフィルタを使う(WordPress 5.7以降、コアやプラグインの出力と相性が良い):

// functions.php
add_filter('wp_robots', function ($robots) {
    if (is_search()) {
        $robots['noindex'] = true;
    }
    return $robots;
});

Enter fullscreen mode Exit fullscreen mode

Next.js(App Router):

// app/search/page.tsx
export const metadata = {
  robots: { index: false, follow: true },
};

Enter fullscreen mode Exit fullscreen mode

インフラ層ではnginx。以下のスニペットには2つの注意点がある。パス形式の検索URL(/search)にマッチし、クエリ形式(?s=)にはマッチしない。クエリ形式には$arg_sで分岐する必要がある。また、nginxのadd_headerには継承ルールがあり、location内でadd_headerを1つ記述すると上位レベルで定義したヘッダーがすべてキャンセルされるため、セキュリティヘッダーなどを再宣言する必要がある。

location /search {
    add_header X-Robots-Tag "noindex" always;
    # 上位レベルのadd_header行(セキュリティヘッダーなど)をここで再宣言
    proxy_pass http://app;
}

Enter fullscreen mode Exit fullscreen mode

詐欺の文脈を置いても、検索結果ページをnoindexするのは標準的なSEO衛生管理だ。重複コンテンツの増加やクロール予算の無駄を防げる。ようやく実行する良い口実になる。

まとめ

  • 日本の警察が警告した「詐欺ではありません」汚染は、サイト検索スパムで説明できる。悪用されているのは仕様だ:クエリを反映し、インデックスを許可する。
  • AIサマリーは検索インデックス上のRAGだ。上流の汚染が下流の回答になる。自サイトの反映面を閉じる方が、AI側のフィルタを待つより速い。
  • noindexが基盤だ。noindexしたいURLをrobots.txtでブロックしてはいけない。検索トラフィックを維持したい場合は、ヒットゼロ時にnoindexを適用する。

サイトを運用しているなら、今日site:yourdomain inurl:searchを試してみてほしい。結果が返ってきたら、上記の防御策が今日の午後の仕事だ。あなたの検索ボックスは、誰かの「詐欺ではありません」を運んでいないだろうか?

参考文献