Google が私に違法賭博サイトをおすすめしてくる

Google が私に違法賭博サイトをおすすめしてくる

この記事は他の言語でも読めます。

Android やモバイル版 Chrome を使っている人なら、ホーム画面の Google フィードやブラウザのトップ画面でおすすめコンテンツを見たことがあるでしょう。この機能は、ユーザーの検索履歴、位置情報、興味などをもとに、そのユーザー向けのコンテンツを表示します。私は世の中のさまざまなニュースを知るために、この機能をよく使っています。

ところが、ある時からタイムラインに怪しいドメインのコンテンツがおすすめされるようになりました。こうしたコンテンツは、ぱっと見ると普通のコンテンツのようにも見えます。私の場合は、主にフロントエンド技術に関する情報記事を装っていました。

しかし記事をクリックしてみると、タイトルとはまったく関係のない違法賭博サイトにリダイレクトされます。最初は報告すれば二度と目にしないだろうと思っていましたが、1 か月以上経っても偽のコンテンツがおすすめされ続けていました。

そこで、おすすめ機能をオフにしてしまおうかと悩んでいたとき、ふと疑問が浮かびました。「こんな偽サイトが、どうやって Google をだましてユーザーに表示されているんだろう?」

この記事では、その疑問を解くために考えたことと実験の過程を紹介します。詐欺サイトがどのような過程を経て私たちのおすすめコンテンツに表示されるのかを一段階ずつ見ていき、最後にそれを防ぐ方法はないか、私の考えをお伝えします。


詐欺サイトの仕組みを突き止める

詐欺サイトの仕組みを把握するために、まずそのサイトにアクセスして開発者ツールを使おうとしました。サイトの HTML 構造と JavaScript の動作を確認すれば、手がかりになりそうだったからです。

しかし、こうした詐欺サイトにはデスクトップのブラウザからアクセスできませんでした。それだけでなく、「Google フィード」を経由しないあらゆる環境でアクセスできませんでした。私がアクセスを試した環境は次のとおりです。

  • デスクトップブラウザ:404 エラー
  • Postman:404 エラー
  • Google フィードからコピーした URL をモバイルブラウザに貼り付け:404 エラー
  • Google フィードからクリックしてアクセス:成功

どの場合もアクセスした URL は同じでした。Google フィードからしかアクセスできない理由はわかりませんが、ひとまずこのサーバーが次の図のように、一般的なブラウザからのリクエストを拒否しているという事実を覚えておきましょう。


HTTP ヘッダーが鍵だ

上のように一般のブラウザをブロックしているのは、おそらく私のように分析しようとするユーザーを防ぐための仕掛けだと思われます。思ったより簡単にはいかなそうですが、ここからわかることが 2 つあります。

  • このサーバーは Nginx を使っています。
  • このサーバーは、Google フィードからのアクセスであることをはっきり検知しています。

Nginx は、リクエストの URL とメソッドが同じでも、HTTP リクエストのヘッダーによって異なる結果を返せます。おそらく Google フィードから遷移してきた場合には特別なヘッダーの値があり、このサーバーはその値がある場合にだけ詐欺サイトへ移動する HTML を返しているのだろう、と推測できます。

では、その「特別なヘッダー」が何なのかを突き止める必要があります。いちばん簡単な方法は、詐欺サイトに正常にアクセスできる状況で、開発者ツールでリクエストの HTTP ヘッダーを確認することです。

しかし先ほど述べたとおり、このサイトにはデスクトップのブラウザからアクセスできませんでした。そこで考えた別の方法は、Android OS からサイトにアクセスし、ルーターや別のサーバーでネットワークトラフィックを傍受することです。例えば次のような方法です。

  • スマートフォンでアクセスし、ルーターでそのリクエストのパケットを解析して HTTP ヘッダーを確認する
  • Android VM の hosts を書き換え、詐欺サイトのドメインへのリクエストをローカルの Nginx サーバーにプロキシする

ところが、これも無理だとすぐに気づきました。SSL 証明書なしではネットワークパケットの中身を確認できませんし、プロキシを使っても SSL 証明書が一致しないのでリクエストが中断されてしまうからです。

最後に試したのは、Android VM と Chrome のリモートデバッグを使う方法です。モバイル版 Chrome のトップ画面でリモートデバッグさえできれば、デスクトップのブラウザと同じようにネットワークリクエストを確認できるだろうと考えました。

結果は、とてもうまくいきました。開発者ツールから、詐欺サイトに正常にアクセスできるリクエストの curl コマンドを取得できました。取得したコマンドは次のとおりです。

curl 'https://alfa.sklep.pl/bucosvpltrhz/' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
  -H 'accept-language: en,ja;q=0.9,ko;q=0.8,en-US;q=0.7,st;q=0.6' \
  -H 'cookie: PHPSESSID=alolor9cv5glrsen734307dqkmsgegg3; fb93c=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoie1wic3RyZWFtc1wiOntcIjUxNFwiOjE3MjE0NzM5MjYsXCIzXCI6MTcyMTQ3MzkyNixcIjUxNVwiOjE3MjE0NzQwNjQsXCIxODRcIjoxNzIxNDc0MDk1fSxcImNhbXBhaWduc1wiOntcIjE5XCI6MTcyMTQ3MzkyNixcIjFcIjoxNzIxNDczOTI2fSxcInRpbWVcIjoxNzIxNDczOTI2fSJ9.ZH6vpMkY0XkaN9vZE30j_6ZQbqk7ZbFnDUyGH6TctVw; _subid=9feaq790q009' \
  -H 'priority: u=0, i' \
  -H 'referer: https://www.google.com/' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?1' \
  -H 'sec-ch-ua-platform: "Android"' \
  -H 'sec-fetch-dest: document' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-site: none' \
  -H 'sec-fetch-user: ?1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36'

これで、PC から詐欺サイトへのアクセスを再現できます。HTTP ヘッダーをひとつずつ確認した結果、このサーバーが Google フィードからのリクエストかどうかを判定していたのは Referer: https://www.google.com/ でした。この HTTP ヘッダーを付けて Postman や curl でリクエストすると、ついに詐欺サイトへリダイレクトする HTML が返ってきます。

<html>
  <head>
    <script type="application/javascript">
      function process() {
        if (window.location !== window.parent.location) {
          top.location = "https://SCAM_SITE.com";
        } else {
          window.location = "https://SCAM_SITE.com";
        }
      }
      window.onerror = process;
      process();
    </script>
  </head>
  <body>
    The Document has moved <a href="SCAM_SITE.com">here</a>
  </body>
</html>

返ってきた HTML は本当にシンプルでした。コードがしているのは、現在のページを別のウェブサイト(違法賭博サイト)に移動させることだけです。ユーザーのブラウザで自動リダイレクトが使えない場合に備えて、リンクもひとつ用意されています。

これで、このサーバーが次の図のように、Google フィードのユーザーに詐欺コンテンツを返していることがわかりました。


クローラーはどうやってだまされたのか?

いよいよ最後の疑問です。上の詐欺サイトの HTML を見ると、本当に何もなく、ただ別のサイトに移動するコードがあるだけです。こんなサイトが、どうやって Google のウェブクローラーに高く評価され、ユーザーにおすすめされるのでしょうか?

答えはやはり HTTP ヘッダーです。Google のような検索エンジンのクローラーは、HTTP ヘッダーに独自の User-Agent を持っています。こうした詐欺サイトは、クローラーがアクセスしたときだけ正常なコンテンツを返し、ユーザーがアクセスしたときは詐欺コンテンツを返すという手口を使っています。

クローラーがこうした詐欺サイトを訪れると、次のような正常な HTML コンテンツを受け取ります。

<html>
  <head>
    <meta name="title" content="最高の JavaScript テクニック 12 選" />
    <meta name="description" content="最高の JavaScript テクニックを紹介します。" />
    <meta property="og:title" content="最高の JavaScript テクニック 12 選" />
    <meta
      property="og:description"
      content="最高の JavaScript テクニックを紹介します。"
    />
    <meta property="og:image" content="/awesome-image.png" />
  </head>
  <body>
    <h1>最高の JavaScript テクニック 12 選</h1>
    <p>最高の JavaScript テクニックを紹介します。</p>
    ...
  </body>
</html>

ここでの HTML は、私が適当に書いた例です。いろいろな方法を試しましたが、この詐欺サーバーから正常なコンテンツを受け取る方法は見つけられませんでした。おそらく、Google が公開している クローラーの IP 範囲 からアクセスしないと、正常なコンテンツは受け取れないのだろうと推測しています。

こうして、この詐欺サイトは全員を完璧にだましました。Google で高く評価されるコンテンツになり、ユーザーにおすすめされます。そしてそれを見ようとしたユーザーは、違法賭博サイトにリダイレクトされます。全体像は次のとおりです。

最後に、この詐欺サイトが動く全体の流れをまとめます。

  1. 詐欺サイトに Google のクローラーがアクセスすると、正常なコンテンツを返す
  2. 正常なコンテンツは、SEO や操作によって Google 検索の上位にランクされる
  3. Google フィードのユーザーに、この URL とクロールした正常な画像やテキストを表示する
  4. ユーザーが Google フィードからアクセスすると、違法サイトにリダイレクトする HTML を返す

どうすれば防げるのか?

ここまでが、私が Google フィードで遭遇した詐欺サイトを追跡した結果です。この過程で意外だったのは、思ったよりシンプルな技術スタックでこうしたサイトを作れるということです。偽のコンテンツと Nginx さえあれば、ドメインを変えながらいくらでも詐欺サイトを作れそうに見えました。

一方で、Google の立場からはこれを防ぐのは簡単ではなさそうです。私が思いついた唯一の方法は、クローラーの情報を隠し、IP 範囲もランダムに変えることです。そうすれば、詐欺サイトはクローラーを検知できなくなります。するとユーザーが受け取る詐欺コンテンツをクローラーが受け取ることになり、そもそもこうしたサイトが Google 検索の上位にランクされることもなくなるはずです。

しかし、これは簡単なことではありません。正常なサーバーでもクローラーの情報が必要な場合があるからです。サーバー側でクローラーと一般ユーザーのトラフィックを区別しなければならないこともありますし、特定の IP 範囲をブロックする際にクローラーを除外するために情報が必要なこともあります。そのため、現在 Google はクローラーの情報を公開しています。

そこで折衷案を考えると、クローラーの情報は公開しつつ、クローラーとユーザーが見るコンテンツが異なっていないかを確認する別のモニタリングシステムを作ることです。少数の匿名クローラーも一緒に動かして結果を比べれば、こうした詐欺サイトを事前にブロックできるのではないかと考えました。

ここで記事を終わります。この問題に遭遇したことがある方や、よい解決策をお持ちの方は、ぜひコメントで考えを共有してください :)

author profile
winetree94

ウェブフロントエンドエンジニア兼、自室の DevOps エンジニアです。
コツコツ書くブロガーになりたいのですが、そうするには怠けすぎています。