【実録調査・専門家向け】アダルトサイトを開くと何が起きているのか?広告追跡の実態をPlaywrightで解析した

調査・研究
やさい
やさい

本記事は、【実録調査】アダルトサイトを開くと何が起きているのか?広告追跡の実態をPlaywrightで解析した | やさしいサイバーセキュリティ  の詳細バージョンです。

もう少し簡単な内容は、上記URLの記事を読んでください。

「スマホで動画サイトを見ていたら、何も押していないのに突然別のページに飛ばされた」──そんな体験をしたことはありませんか。あるいは、一度調べた商品の広告がまったく別のサイトでも出てくることに気づいていますか。Webページを開く瞬間、裏側では私たちが意識していない大量の通信が走り、広告主・計測サーバー・アフィリエイト業者が連携して動いています。本記事では、ブラウザ自動化ツール「Playwright」を使って実際にネットワーク通信を記録・分析し、広告エコシステムの多層構造を可視化した過程を記録します。観測されたデータは実際のログに基づいており、個社・個人が特定できる情報はすべて ○○ に置き換えています。

⚠ 注意

本記事は広告エコシステムの技術構造を教育・研究目的で解説するものです。特定企業・サービスへの攻撃や不正アクセスを目的とするものではありません。観測された通信の多くは各国の法規制の範囲内での広告配信活動です。観測結果に基づく事実記述であり、個別企業の経営判断・意図への評価は含みません。

調査環境と手法

使用ツール:Playwright

Playwrightは Microsoft が開発・公開しているオープンソースのブラウザ自動化ライブラリです。本来はWebアプリケーションの自動テストに使われますが、ページ読み込み時のすべてのネットワークリクエストをイベントとして取得できるため、通信ログの記録にも活用できます。

Python環境への導入手順は以下のとおりです。

pip install playwright
playwright install chromium

記録できる情報は「どのURLへ」「どんなリソース種別(script / image / xhr / fetch / document など)で」通信したか、という公開情報のみです。TLSの復号や認証情報への干渉は行っていません。

調査対象と記録条件

項目 内容
調査対象サイトA 欧米向けアダルト動画サイト(英語圏)
調査対象サイトB 日本向けアダルト動画サイト(日本語圏)
アクセス方法 Chromiumブラウザで各動画ページを1件ずつ表示
記録時間 ページ読み込み後15秒間
UA(ユーザーエージェント) iPhoneを模したモバイルUA(スマホ広告の挙動観察のため)
ロケール ja-JP / Asia/Tokyo
保存内容 URLとリソース種別のみ(コンテンツ本体は保存せず)

【サイトA】欧米向けサイトの広告エコシステム

通信ログの全体像

サイトAをロードしてから15秒間で記録された外部通信は以下のとおりでした。

観測ドメイン(カテゴリ) 通信回数 リソース種別 役割
ランダムサブドメイン系配信サーバー(○○.net) 42回 image / stylesheet / script 広告クリエイティブ配信(CDN)
サイト自身のメディアCDN 24回 media 動画ファイルの配信
○○広告サーバー(入札API) 7回 xhr RTBオークションのやり取り
○○広告サーバー(スクリプト) 1回 script 広告スクリプトの初期化
Google Analytics 2回 fetch アクセス解析
Google Tag Manager 1回 script タグ管理
○○(CDNプロバイダ) 1回 script パフォーマンス計測

合計:外部ドメイン 7件 / 外部リクエスト 78件

RTBオークション(リアルタイム入札)の動作を観測

XHRで7回通信していた ○○広告サーバー の /v1/api.php エンドポイントへの通信がRTBオークションです。レスポンスをデコードすると、以下の構造が確認できました。

{
  "zones": [{
    "idzone": ○○○○,        // 広告枠の識別ID
    "type": "banner",       // 広告タイプ
    "data": {
      "html": "<iframe ... ○○追跡ドメイン/s1/..."
    }
  }]
}

このレスポンスを解析すると、1ページに計7つの広告枠(banner × 3、native_ad × 3、video_slider × 1)が設置されており、それぞれに独立した idzone(広告枠ID)が割り当てられていることがわかります。RTBのフローは次のとおりです。

ステップ 処理内容 観測された通信
① スクリプト読み込み ○○広告スクリプトを初期化 script リクエスト × 1
② 入札リクエスト SSPが各DSPへ入札を依頼 XHR /v1/api.php × 6〜7回
③ 落札・クリエイティブ配信 落札した広告素材(動画・画像)を配信 image × 42回(CDN経由)
④ インプレッション計測 広告表示をサーバーに記録 /cimp.php への通信
⑤ クリック追跡準備 iframeにリダイレクトURLを埋め込み iframe内のdata-link属性
⚠ 注意

インプレッション計測(/cimp.php)のパラメータに data=H4sI... という gzip 圧縮された文字列が付いていました。この中にはユーザーの環境情報(画面サイズ・UA・タイムゾーン等)が格納されていると考えられます。これは広告業界では標準的なビッドストリームデータの送信方法です。

クリック後のリダイレクトチェーン(サイトA)

広告iframe内の data-link 属性に設定されたクリック追跡URLには、以下のパラメータが含まれていました。

パラメータ 値(例) 意味
cv7 調査対象サイトA のドメイン 流入元サイト
cv2 ○○○(数値) キャンペーンID
cv4 300×250 広告サイズ
cv6 ○○○○○(数値) 広告主ID

これらの情報は広告主が「どのサイトの・どのサイズの広告枠から・何件クリックがあったか」を把握するために使われます。

地域ターゲティングの証拠

動画広告クリエイティブのURLパラメータに地域を示す文字列が含まれていることを確認しました。日本語圏からのアクセスに対して日本ユーザー向けのパラメータが付与された広告素材が配信されており、広告ネットワークがIPアドレスから地域を判定して配信内容を切り替えていることが読み取れます。これは「ジオターゲティング」と呼ばれる標準的な広告技術です。

また、観測された欧米系大手広告ネットワーク(○○)は「ホワイトラベル」と呼ばれる別名義で複数のサイトに配信しており、エンドユーザーから見ると異なる配信元に見えますが、実態は同一のプラットフォームであることが確認できました。

【サイトB】日本向けサイトの広告エコシステム

通信ログの全体像

サイトBはサイトAと比較して関与する外部プレイヤーが大幅に多く、より複雑な構造をしていました。

観測ドメイン(カテゴリ) 通信回数 種別
国内広告ネットワーク(DSP系)○○ 47回 script / image
同上(データ同期) 47回 image / script
国内大手広告ネットワーク ○○AD 41回 image / script
フランス系大手リターゲティング ○○ 38回 document / fetch / script
国内大手広告ネットワーク ○○AD(クッキー同期) 32回 image / script
国内DSP ○○ 32回 script
国内広告最適化 ○○ 30回 fetch / script
国内DSP ○○(別系統) 24回 image / script
国内DMP(データ管理基盤)○○ 21回 script
国内SSP(入札)○○ 17〜21回 image / fetch
国内広告系(バイパス計測)○○ 17回 image
正体不明ドメイン ○○.link 11回 script
広告中継サーバー ○○.com(JavaScriptのみ) 9回 script / document
大手DSP 入札同期 ○○ 7回 image
国内広告ネットワーク ○○(大手系) 6回 xhr / script
送客仲介サイト ○○.com(アフィリエイト) 3回 document / media
Google Analytics / Tag Manager 4回 script / fetch
その他(フォント・jQuery・CDN等) 複数 font / stylesheet / script

合計:外部ドメイン 51件 / 外部リクエスト 598件

サイトAとサイトBの比較

比較項目 サイトA(欧米向け) サイトB(日本向け)
外部ドメイン数 7 51
外部リクエスト数 78 598
主要広告基盤 欧米1社が集中支配 国内複数社が分散参加
リターゲティング クリック追跡中心 DMP・Cookie同期あり
収益構造 広告収益のみ 広告+アフィリエイト複合
外部iframe なし 複数(送客・クッキー同期)
不審なスクリプト なし ○○.link(難読化)

外部iframeの構造

サイトBのページには以下の外部iframeが埋め込まれていることが確認できました。

iframe読み込み元 目的
○○中継サーバー(○○.com) サイトB専用の広告ラッパー(iOS向けスクリプトを展開)
送客仲介サイト ○○.com(affid=○○○○) アフィリエイトIDを付与した送客コンテンツ(2件)
フランス系大手 ○○ のクッキー同期フレーム リターゲティング用のユーザーID照合(4件)

特に送客仲介サイトのURLには affid=○○○○ というパラメータが付いており、サイトBのアフィリエイトIDです。このiframe経由でユーザーが外部の有料サービスに流入し会員登録・購入をした場合、サイトBに報酬が発生する仕組みになっています。

広告スロットの集中分析

サイトBの主要な広告配信を担っていた ○○中継サーバー(○○.com)が提供するスクリプト(iOS向け)を取得し、内部に定義されていた広告タグを抽出した結果、以下のことが判明しました。

広告タグの配信先 スロット数 構成比
国内広告プラットフォーム ○○SSP 96 98.0%
国内広告ネットワーク ○○.com 1 1.0%
別系統広告ネットワーク ○○ 1 1.0%
合計 98 100%

1スクリプトファイルに98個の広告タグが定義され、そのうち96個(98%)が単一の広告プラットフォーム(○○SSP)に集中していました。これは ○○中継サーバーが表向きの配信窓口として機能しつつ、実態は ○○SSP がサイトBの広告枠のほぼ全量を担っていることを示しています。

⚠ 注意

この集中構造は「1社が広告枠を独占することでフィルフルメントを最大化する」という広告業界での標準的な戦略の結果です。必ずしも技術的問題を意味するわけではありませんが、広告主の観点からはブランドセーフティ上の注意点(どのサイトに広告が出るかコントロールが難しい)があります。

注目スクリプト:○○.link の解析

発見の経緯

サイトBの通信ログを整理していると、同一のスクリプトファイルが11回繰り返し読み込まれているドメイン(○○.link)を発見しました。同一スクリプトが短時間に反復読み込みされる挙動はセキュリティ上の観点から注意を要するパターンです。スクリプト本体をPythonスクリプトで取得し(サイズ:2,310バイト)、内容を確認しました。

難読化の特徴

取得したコードには、以下のような特徴的な難読化処理が施されていました。

(function(_0x22892f, _0x4bc62b) {
  var _0x44dc9e = _0x540e, _0x52d4b0 = _0x22892f();
  while (!![]) {
    try {
      var _0x2ebd1d =
        parseInt(_0x44dc9e(0x1af)) / 0x1 *
        (-parseInt(_0x44dc9e(0x1b3)) / 0x2) + ...
      if (_0x2ebd1d === _0x4bc62b) break;
      else _0x52d4b0['push'](_0x52d4b0['shift']());
    } catch(e) { _0x52d4b0['push'](_0x52d4b0['shift']()); }
  }
})(_0x540e, 0x○○○○○);

難読化の特徴を整理します。

難読化の手法 内容
変数名の全置換 _0x... 形式のランダムな名前に変換
文字列の分散格納 文字列を配列に分解し、インデックスで参照
整数演算によるフロー撹乱 parseInt の連鎖計算で配列の並び替えを制御
while(!![]) ループ 静的解析ツールによる読み取りを妨害

この種の難読化は公開されている JavaScript難読化ツールを使って生成できます。なお、難読化は著作権保護・ライセンス管理・パフォーマンス改善などの合法的な目的でも使われます。ただし正規の広告配信スクリプトで同一ファイルが11回読み込まれる理由は通常考えにくく、解読を試みました。

デオブファスケーション(難読化の解除)と動作の解読

公開されているデオブファスケーターを使って元のコード構造に近い状態に復元した結果、このスクリプトの動作が判明しました。

// 動作の概要(復元後コードを人間が読める形に整理)
 
(function(window, document) {
 
  // 現在のスクリプトタグの親要素を取得
  var parentEl = document.currentScript.parentElement;
 
  // 親要素内の最初のiframeを監視対象にセット
  var targetIframe = parentEl.getElementsByTagName("iframe")[0];
 
  targetIframe.addEventListener("load", function() {
    var iframeDoc = targetIframe.contentWindow.document;
 
    // iframeの中に特定の広告主ID(○○)が含まれているか監視
    function watchForAdId() {
      if (/特定広告主ID/.test(iframeDoc.body.innerHTML)) {
        var links = iframeDoc.getElementsByTagName('a');
 
        for (var i = 0; i < links.length; i++) {
          if (/特定広告主ID/.test(links[i].href)) {
 
            // スマホのタッチ終了イベントを横取り
            links[i].querySelector('img').addEventListener('touchend', function() {
 
              // ページ全体(_top)を強制的にリダイレクト
              window.top.open(links[i].href, '_top');
            });
            return;
          }
        }
      }
      // IDが見つかるまで10ミリ秒ごとに再チェック
      setTimeout(watchForAdId, 10);
    }
    watchForAdId();
  });
 
})(window, document);

このスクリプトが行っていること

動作要素 詳細
発動条件 特定の広告主ID(○○)を含む広告がiframeに表示されたとき
対象デバイス スマートフォンのみ(touchend イベント使用)
動作内容 広告画像へのタッチを検知し、ページ全体(_top)を広告リンク先へ遷移
ポップアップブロック回避 window.top.open(url, '_top') でポップアップでなくページ遷移として処理されるため、多くのポップアップブロッカーをすり抜ける
発動タイミングの制御 10ms間隔のポーリングで広告IDの出現を待機
🚨 重要

このスクリプトは「スマホで何もボタンを押していないのに突然別のページに飛ばされた」という体験を引き起こす仕組みの典型例です。ただし本スクリプト単体の目的・意図については、コードの挙動から読み取れる範囲の観察事実を記載しています。

○○SSP による類似スクリプト(別ファイル)の確認

○○SSP が配信していた別のスクリプトファイル(約8,700バイト)にも同様の難読化処理が確認されました。こちらを復元した結果は以下のとおりでした。

// ○○SSPスクリプト(復元後・概要)
 
// iOSデバイスかチェック
if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
 
  // 広告コンテナ内のすべてのリンクを取得
  var links = findAllLinks(body, 'a', []);
 
  for (var i = 0; i < links.length; i++) {
    links[i].addEventListener('touchstart', function(e) {
      e.preventDefault(); // 本来のクリック動作をキャンセル
 
      // ページ全体を強制遷移
      window.top.open(this.parentNode.href, "_top");
    });
  }
 
  // クッキー(_ckglrt)で一定時間内の連続発動を制限
  if (!isFrequent(cookieKey, timeLimit)) {
    setFrequencyCookie(cookieKey, maxAge);
    // → リダイレクト実行
  }
}

このスクリプトの動作特性をまとめます。

特性 内容
対象OS iOS(iPhone / iPad / iPod)限定
トリガー touchstart イベント(タッチ開始と同時に発動)
発動抑制 クッキー(_ckglrt)に発動時刻を記録し頻度を制限
ユーザーへの見え方 クッキーの有効期間が切れたタイミングでランダムに発動するため「なぜ飛んだかわからない」状態になりやすい

リダイレクトチェーンの完全追跡

サイトBの広告クリック後にどこへ到達するのか、Playwrightで実際にリンクを取得し、Pythonでリダイレクトを手動で追跡しました。確認できたルートは3系統です。

ルート①:有料動画サービスA への送客

ステップ URL(概略) HTTPステータス
START 送客仲介サイト ○○.com / Click○○ / ○○-○○-affid
1 ○○計測サーバー / Click○○ / ○○-affid 301
2 ○○アフィリエイト計測 ?md5=○○&r=有料サービスA&dtiAff2=○○ 302
GOAL 有料動画サービスA のトップページ 200

ルート②:有料動画サービスB への送客

ステップ URL(概略) HTTPステータス
START 送客仲介サイト ○○.com / Click○○ / 別IDパターン
1 ○○計測サーバー / Click○○ 301
2 ○○アフィリエイト計測 ?md5=○○&r=有料サービスB&dtiAff2=○○ 302
GOAL 有料動画サービスB の無料体験ページ 200

ルート③:マッチングサービスへの送客

ステップ URL(概略) HTTPステータス
START 国内広告配信 ○○.jp / pa.do?s=○○&o=○○
1 LP配信サーバー ○○.net / tp_○○マッチングサービス?s=○○&afid=○○ 302
GOAL ○○(マッチングサービス)の登録ランディングページ 200
⚠ 注意

いずれのルートも各ステップで流入元(サイトB)・キャンペーンID・広告主IDがパラメータに記録されています。これにより「どのサイトの・どの広告から・何人が流入したか」が広告主・仲介業者・サイト運営者それぞれに把握される仕組みになっています。アフィリエイト広告の仕組みとして標準的な慣行です。

サイトBの収益構造まとめ

収益源 仕組み 主な送客先
RTB広告収益 ページ表示のたびに広告枠を入札で販売 ○○SSP経由の各広告主
アフィリエイト(動画系) 有料動画サービスの会員登録で成果報酬 有料動画サービスA・B
アフィリエイト(マッチング系) マッチングサービスへの会員登録で成果報酬 ○○(マッチングサービス)× 2パターン

広告エコシステム全体図

今回の調査で判明したサイトBの広告エコシステム全体像を整理します。

サイトB(日本向けアダルト動画)
広告収益レイヤー

○○中継サーバー(サイトB専用ラッパー)

○○SSP(98%の広告枠を担当)

国内SSP各社(入札参加)
国内DSP各社(入札参加)
フランス系大手 ○○(リターゲティング)
スクリプトレイヤー

○○中継サーバーのiOS向け広告スクリプト

○○SSPのリダイレクトスクリプト(難読化)
→ iOSタッチ操作を横取り

○○.link スクリプト(難読化)
→ 特定広告表示時にtouchendをインターセプト
アフィリエイトレイヤー

送客仲介 ○○.com(affid=○○○○)

○○計測サーバー

○○アフィリエイト計測

├ 有料動画サービスA
├ 有料動画サービスB
└ ○○(マッチングサービス)× 2

この調査から学べること

① 「無料サービス」の裏にある多層的な収益構造

無料でコンテンツを提供するサイトは、RTB広告収益とアフィリエイト送客収益を組み合わせることで運営されています。ユーザーは代金を払っていませんが、行動データと「有料サービスへの誘導」という形で対価を提供していることになります。

② スマートフォンユーザーは積極的な広告手法の主なターゲット

今回確認したタッチイベントを利用したリダイレクト手法はPC環境では機能しません。スマートフォンの操作特性(タッチ操作・ポップアップブロッカーの動作の違い等)を利用した広告手法が実装されており、モバイルユーザーが優先的なターゲットになっています。

③ 難読化スクリプトの普及

今回の調査では2つの異なるドメインのスクリプトで難読化処理が確認されました。難読化自体は合法・正規の目的でも使われますが、コードの動作を確認せずにスクリプトを読み込んでいるサイト運営者(ブログ運営者等)は意図しない挙動を利用者に提供してしまうリスクがあります。

④ 一般ブログにも同じ広告インフラが配信されるケース

今回の調査で確認された国内広告プラットフォームの一部は、一般ブログ運営者への広告掲載営業も行っています。広告ネットワークは複数のサイトカテゴリにまたがって広告を配信することが多く、ブログ運営者は自分のサイトに配信されるスクリプトの全動作を把握しにくい状況があります。

ユーザーができる対策

✅ 対策済み確認

以下の対策を実施することで、不意なリダイレクトや過剰な追跡のリスクを軽減できます。

  • 広告ブロッカーの導入(PC):uBlock Origin 等のブラウザ拡張を使用する
  • コンテンツブロッカーの導入(スマホ):AdGuard for iOS / Android 等を使用する
  • プライベートDNSの活用:Cloudflare 1.1.1.2・NextDNS 等でDNSレベルのブロックを設定する
  • サードパーティCookieをブロック:ブラウザ設定でトラッキング防止を有効化する
  • 突然の遷移にはすぐ「戻る」:意図せずページが切り替わったら前のページに戻り、表示された遷移先でフォーム入力・決済は行わない
  • サイトの信頼性を確認:見慣れないURLに飛ばされた場合はそのページで個人情報を入力しない

法的・倫理的観点

本調査の行為は以下の範囲に留まっており、不正アクセス禁止法の対象となる行為(認証突破・アクセス制限の回避等)は行っていません。

行為 実施 理由
公開ページへのブラウザアクセス ✅ あり 誰でも同じ操作が可能な範囲
ネットワーク通信ログの記録 ✅ あり 開発者ツールで手動でも確認できる情報
スクリプトファイルの取得・解析 ✅ あり 公開URLから取得可能なファイル
コンテンツ(動画・画像)のダウンロード ❌ なし 著作権・規約上の懸念あり
認証の突破・ログインページへのアクセス ❌ なし 不正アクセス禁止法の対象
連続的な大量アクセス ❌ なし DoS的行為とみなされる可能性
⚠ 注意

同様の調査を自分で実施する場合は、対象サービスの利用規約を必ず確認してください。スクレイピング・自動アクセスを明示的に禁止しているサービスもあります。また本記事内のスクリプト取得手順は学習目的での参考情報であり、特定サービスへの適用を推奨するものではありません。

まとめ

Playwrightを使った今回の調査で、以下のことが実データとともに確認できました。

  • 欧米向けサイト(サイトA)は外部ドメイン7件・78リクエストで、RTBを核とした比較的シンプルな構造
  • 日本向けサイト(サイトB)は外部ドメイン51件・598リクエストと大幅に複雑で、国内広告プレイヤーが多数参加
  • 広告スロット98個のうち96個(98%)が単一プラットフォームに集中するケースが確認された
  • スマートフォンの touchend / touchstart イベントを横取りして強制リダイレクトを発生させるスクリプトが複数確認された
  • 広告クリックから最終サービスまで3〜4段階のリダイレクトが挟まり、各段階で流入元・クリック情報が記録されている

Webページの裏側では、私たちが意識していない多数のプレイヤーが動いています。広告エコシステムの構造を知ることは、ユーザーとして適切な対策を取るためにも、またWebサービスの開発者・運営者として自分のサイトを正しく理解するためにも、意味のある知識です。

コメント