1 ポイント 投稿者 GN⁺ 2025-03-19 | 1件のコメント | WhatsAppで共有
  • Apple TVとインターネットの間にpfSenseベースのMITMプロキシを置き、HTTPSを復号したうえで、YouTubeが返すProtobufレスポンスを改変し、Appleデバイスで広告スロットが登録されないようにしたPoCである
  • 従来のDNSブロッカーやVPNルーティングは、YouTube広告と本編動画が同じドメイン・インフラを使う構造のため、DNS TTL、IP不一致、403エラー、ASNリークといった限界に突き当たった
  • Squidの実験は性能と設定の問題で中止され、その後FreeBSD jailのmitmproxy/mitmdumpでTLSトラフィックを復号し、JSON・Protobufレスポンスを直接書き換える方式へ移行した
  • Web版YouTubeではJSONのadPlacementsplayerAdParamspagead URLなどを削除できたが、iOS版YouTubeアプリではapplication/x-protobufレスポンスに広告スロットと追跡情報が含まれており、Protobuf構造を扱う必要があった
  • 最終方式は、/pagead/文字列の近くでfield tagを逆方向に探し、50195462のようなタグをtarget_field_tag - 1に変える線形スキャンと1バイト修正であり、完全なデコードなしでもリアルタイム処理に見合う性能を出した

目標と初期ネットワーク設計

  • 目標は、FreeBSDとpfSenseベースのルーターを構築し、Apple TVとiPhoneのpre-roll、mid-roll、end-roll YouTube広告をネットワーク全体でブロックすることだった
  • Apple TVと外部インターネットの間にman-in-the-middleプロキシを置けば、HTTPSトラフィックを復号し、GoogleがYouTube広告の配信に使うProtocol Bufferデータを読める
  • 数か月にわたってYouTube広告ブロックを実装した後、YouTube Premiumの支払いを始めており、「できること」と「すべきこと」は別だと述べている
  • 広告・追跡をブロックする理由として、個人情報の追跡、帯域幅の浪費、clickbait、cryptojackingを挙げている
    • ネットワークトラフィックの25〜40%が、広告、追跡スクリプト、fingerprint.js、googletagmanager.js、Hotjarのようなリアルタイム分析ローダーになり得るとみている
    • CoinHive.jsのようなcrypto-mining JavaScriptは、コンピュータを過熱させたり悪用して少額を稼いだりできると説明している

pfSenseハードウェアと基本設定

  • SMBネットワーク全体を保護するには、VM、Dockerイメージ、Raspberry Piでは性能が不足するため、パケットのルーティング・復号・監視だけを担う専用ハードウェアが必要だとみている
  • 使用したルーターハードウェアは、AES-NI命令セットを備えたmini PC、DDR4 RAM、mSATA SSD、pfSense書き込み用USBドライブだった
    • 構成例はJ4125 mini PC、32GiB DDR4 RAM、128GiB mSATA SSD
    • 128GBのストレージは、ログ、SSD摩耗の低減、パケットキャプチャ、NPM・Dockerエッジキャッシュ用途に十分だとしている
  • pfSenseのインストールイメージは約360MBで、Etcher AppImageを使ってUSBドライブに書き込める
  • 初期設定後、AES-NIが「Yes (inactive)」と表示されたため、System › Advanced › Miscellaneousで手動で有効化した
  • 32GiB RAMを活用して/var/tmpに十分なRAMディスクを割り当て、128GiB SSDにはwear-levelingを期待しつつ、RAMディスクのバックアップを毎時実行するよう設定した
  • DashboardにはS.M.A.R.T.ウィジェットを追加し、SSDの異常を検知できるようにした

DNSブロック、ネットワーク分離、pfBlockerNG

  • 以前はRaspberry Pi上のPi-holeをDNSレベルの広告ブロッカーとして使っており、pfSenseではpfBlockerNG-develを導入して広告・悪性コンテンツのブロックやgeo-blockingを試した
  • pfb_dnsblサービスが起動しない、またはstatusタブに[ Missing CRON task ]と表示される場合は、空ファイル/var/run/bootingの削除を試すよう勧めている
  • mini PCの3つのGigabitポートを活用してVLANではなく物理的に別ネットワークを構築し、AlexaやApple TVのような“phoning-home”デバイスをメインネットワークから分離した
    • untrustedデバイスは172.31.1.0/24のプライベートネットワークに配置した
    • trusted LANは192.168/16に維持した
    • IoT向けハードウェアLANはadblockerを経由させ、1.1.1.19.9.9.9のようなhard-coded DNSクエリを横取りして、YouTubeがDNSブロッカーを回避できないようにしようとした
  • pfSense配下のすべてのクライアントがローカルのUnbound DNSサーバーを使うよう、NATルールを構成した
    • DNS over TLSを先に遮断しないとDNSクエリの横取りはできないとみている
    • iPhoneは暗号化DNSトラフィックの遮断に対するPrivacy Warningを表示することがあるが、上流DNSへの問い合わせ自体はCloudflareへ暗号化されるとしている
    • NAT reflectionは無効化し、外部インターネットがDNSサーバーにアクセスできないようにすべきだとしている
  • Non_WANファイアウォールaliasを作成し、WAN以外のインターフェースからのローカルDNSクエリ53番ポートをlocalhostへリダイレクトした
  • YouTubeは広告と本編動画を同じドメインから配信するため、pfBlockerNGやPi-holeのようなドメイン名ブロッカーでは広告だけをフィルタリングしにくかった

VPN迂回実験と失敗点

  • 広告ブロックではなく、YouTube広告アルゴリズムを欺いて広告主にとって魅力の低いユーザーに見せる実験も行った
    • pfSenseルーターでYouTubeの位置追跡トラフィックをVPN経由で視聴者の少ない地域へルーティングしようとした
    • YouTubeアカウント上で「70歳男性、イタリア在住」と認識させることを目標にした
  • pfSenseではOpenVPNではなくWireGuardを使い、Apple TV全体のトラフィックをVPNへ流す基準実験を行った
    • FreeBSDのWireGuardパッケージをインストールし、トンネルを追加・有効化した
    • NordLynx設定のため、Linux VMでsudo wg showconf nordlynxを実行してprivate keyを確認し、pfSenseへ移した
  • テストの結果、ノートPCのGoogleはイタリア語表示になり、Apple TVのYouTubeもイタリア語に変わった
    • 広告は依然として一部表示されたが、以前よりは減ったという
    • NetflixとAmazon Primeでは問題が発生し、CSSやfontファイルが遮断されたり、thumbnailが読み込まれなかったりしているように見えた
    • Apple TV全体のトラフィックをVPNへ流すべきではないと警告しており、NetflixとPrimeはVPNプロバイダーやgeofencingを高精度で検知するとみている
  • その後、Apple TVのYouTubeトラフィックのみをVPNに載せるため、www.youtube.comyoutube.comgooglevideo.comaccounts.google.comgoogleapis.comgstatic.comなどを対象にファイアウォールのポリシールールを構成した
    • 結果として、YouTubeはユーザーをミラノにいるとみなし、NetflixとPrime Videoはカナダにいるとみなした
    • 広告は「few and far between」と言える程度まで減ったという
  • その翌日、DNS race conditionが発生した
    • pfSenseのhostname aliasはデフォルトで300秒ごとにresolveされる
    • YouTubeのDNS TTLは1,440秒、つまり24分である場合がある
    • Alias DaemonがresolveしたIPと実際のクライアントが受け取ったIPが異なると、ポリシーがYouTubeトラフィックをトンネルできないことがある
  • 一部のYouTube動画は403 Forbiddenで再生できなかった
    • YouTubeは各googlevideo.comリクエストにユーザーのIPをembedしているという
    • 変形されたr5---sn-hpa7kn76.googlevideo.comのようなドメインがトンネルされないと、誤ったIPからリクエストが送られて問題が起きる
    • 必要なのは*.googlevideo.comのwildcardトンネリングだが、NATとファイアウォールルールはwildcardホスト名ではなくIPで動作する

DNSクエリベースのIP追跡PoC

  • *.googlevideo.com をVPN経由でルーティングするため、Google Video DNS query hijack 方式を構想した
    • DNS query logを定期的に追跡し、*.googlevideo.com のqueryをaliasリストに追加する方式である
    • 各videoが固有で変形されたdomainを使うなら、この方式はvideoごとに更新しない限り機能しないと見た
  • 新たな目標は、Python 3とpfSense REST APIでDNS queryを監視してIPを捕捉し、応答を一時保留したうえでVPN tunneling ruleにIPを追加し、DNS replyをreleaseすることだった
  • pfSense REST APIをインストールし、https://pfsense/api/v1/firewall/alias にGETリクエストを送って VPN_domains aliasを照会した
  • Unbound DNS ResolverのPython moduleを調べ、DNS-query messageをlogすることに成功した
    • 現在のPythonバージョンは3.8だった
    • Unbound Python moduleのサンプルはPython 2.4ベースだったため、2to3 や整形が必要かもしれないと見た
  • PoC scriptはDNS responseからA/AAAA recordのIPを抽出し、pfSense aliasに追加する
    • Aレコードは ipaddress.IPv4Address(d.rr_data[j][2:]).exploded で処理した
    • AAAAレコードは ipaddress.IPv6Address(d.rr_data[j][2:]).exploded で処理した
    • alias TTLは1時間、capacityは500に設定した
  • 翌日、Unbound DNS Resolverが segfault し、IPを追加するたびにpfSense ruleをreloadする必要があったため、pfSenseは非常に遅くなった

Squidからmitmproxyへ移行

  • 新たな目標は、Squid系proxyを調査・導入し、信頼された偽CA certificateを作成してTLS trafficを復号する方向へ変わった
  • Squidの実験では、pfSense packageとして提供される squid3 proxyが要件を満たすか試した
    • /squid_cache 専用フォルダを作り、cacheサイズを8GiBに設定した
    • Transparent HTTPS supportを期待した
  • SquidとSquidGuardを1日かけて設定した後、断念した
    • 速度が非常に遅かった
    • ACL設定が煩雑だった
    • https://http/* 関連のissueがあった
    • SquidGuardのURL filter list updateに非常に時間がかかった
    • SquidのUIが不十分だった
  • その後、Pythonで書かれた mitmproxy を使うことにした
    • Python hookの拡張性とUIのため、SSLSplit の代わりにmitmproxyを選んだ
    • pfSenseのFreeBSDバージョンは 12.2-Stable、64-bit buildだった
  • pfSenseの標準環境ではjailがdisabledだったため、ezjail を手動でインストールし、mitmproxy 用のjailを作成した
    • ezjail-admin create mitmproxy 'lo0|127.0.1.1' でjailを作成した
    • transparent proxy modeのために allow.raw_sockets=1 を設定した
    • raw socketが塞がれていると、Transparent mode failure または Cannot open connection, no hostname given. のようなエラーが出る可能性があるという
  • Linux tarball binaryの実行はFreeBSDで失敗した
    • ELF interpreter /lib64/ld-linux-x86-64.so.2 not found が発生した
    • libdl.so.2, libz.so.1, libpthread.so.0, libc.so.6 も見つからなかった
  • jail内で pkg install mitmproxy を実行したところ、インストールには50個のパッケージ、追加容量206MiB、ダウンロード33MiBが必要だった
  • MITMProxyをLANからアクセス可能にするため、127.0.1.1 のvirtual IPをlocalhostに割り当て、NAT ruleで [Private IPs]:8080127.0.1.1:8080 に一時転送した
  • MITMProxyが自動生成するCA PEMファイルは ~/.mitmproxy/mitmproxy-ca-cert.pem で、このCA certをテスト端末のTrusted Root Storeにインストールした
  • mitmproxy はidle状態でもCPU使用率が高く、リクエストごとのTLS certificateリアルタイム生成と過剰なloggingが速度を大きく低下させると見た
    • mitmdump はUIと過剰なloggingを省くため、CPU負荷がより低いと見た

Web版YouTube JSON広告の除去

  • Certificate Pinningは、serverまたはclientが想定するcertificate fingerprintをあらかじめ知っているため、MITMProxyのcertificate偽装が通用しない手法である
  • 問題のあるhostは --ignore-hosts オプションでproxyを迂回させられる
    • 例として apple.com:443, icloud.com:443 をignoreした
  • YouTubeアクセス中、page adsが暗号化されていないheaderとともにMITMProxy上で見え、単純なregexによる遮断の可能性を検討した
  • YouTube ad block scriptを適用するため、mitmdump--scripts "youtube.py" を追加した
  • smoke-test filterはURL substringを基準に広告リクエストを遮断する
    • youtube.com: /pagead/, /log_event?, /stats/ads, /stats/qoe?, /ptracking?, /generate_204, el=adunit, adformat=, /activeview?
    • google.com, google.ca: /pagead/
    • ggpht.com: .
  • 遮断しようとしていたリクエストはMITMProxyとDevToolsのNetwork panel上で実際に遮断されたように見えたが、adsは依然として表示され、ときにはadsが自動でskipされたり再生失敗になったりした
  • その後、JSON payload内で広告関連URLをかなり発見した
  • YouTube UIとHTTP workflowをcookiesやservice workersまで分析した結果、pre-roll、post-roll、mid-video広告をすべて除去できるようになったという
  • この段階では、routerで YouTube Web広告のJSON payload からadsを除去できた

iOS版YouTubeとProtobufの問題

  • YouTube iOSアプリは、Web版と同じAPIコールのProtobuf versionで非常によく似たデータを表示していた
  • Protobufではキーが数値で、変更される可能性もあるため、JSONPathでadvertisement sectionを見つける方式は使えない
  • YouTubeは、これから表示するadsの大きな一覧をpayloadとして送り、その一覧を消費し切ると、また別の大きな一覧がすぐ届くという
  • Protobuf payloadでは、“Telus,” “Samsung TV,” “Boxing Week,” “Buy now”のような文字列が見えていた
  • iOS版YouTubeのprotocolはWeb trafficとは異なっていた
    • Web版では、URLとrange query parameterを見て、ad videoと目的のvideoをある程度区別できた
    • iOS protocolはrange query parameterもRange headerも使わず、video chunkに&nr=2&nr=3のようなcounterを使う
    • iOSでadをブロックするには、Protobuf responseをリバースエンジニアリングする必要があった
  • decoded Protobuf messageからhas_unlimited_entitlement: Falsehas_premium_lite_entitlement: Falseという項目が見つかったが、これをtoggleする代わりにheuristicsへ戻った
  • 生のProtobuf約500KiBをPythonのpure implementationでdecodeするのは非常に遅かった
    • i7-6700 desktopでのPythonの結果は約2.06〜2.11秒だった
    • pfSense routerでのPythonの結果は約22.8〜24.2秒だった
    • C++ protoc --decode_rawはdesktopで約0.017〜0.022秒、pfSense routerで約0.12〜0.14秒だった

Protobufのデコードとスキーマ抽出の試み

  • Pythonではraw Protobuf decodingがサポートされていないため、C++ libprotobuf.soを直接使う代わりに、subprocess.PopenでC++ protocバイナリと通信する方式を選んだ
  • 広告動画の応答をfuzzingし、空の200404503、切り詰めたresponse body、広告動画の一部をnull化するなどを試したが、iOSアプリは遅くなった後にcrashするか、広告画面で停止した
  • URLのブロックはアプリの対抗動作を引き起こし、video response chunkにはsession metadataも含まれていた
  • Burp Suite向けのblackboxprotobufは、raw Protobuf wire messageをdecodeし、内容を注入した後に再びencodeして、Protobuf endpointの動作を確認できるようにしてくれた
    • PyPI forkではなくoriginalのBurp Suite versionを使うべきだという
    • 一部のforkではdeep recursionのため、stack overflowやinfinite recursionの問題がある
    • C++ bindingsを使えば、約500KiBのraw Protobufを数秒でtranscodeできる
  • 生成されたschemaは完全ではなく、大きく深くネストされており、pretty-printも遅かったが、広告の詳細情報を探すには十分だった
  • Android版YouTube APKから実際の.protoまたはschema fileを抽出しようとして、PBTK、Apktool、dex2jar、Java Decompilerを試した
    • PBTKが抽出したのは59-byteのproto fileだけだった
    • Java内にProtobuf classesやgetter/setterはあったが、true schema filesは得られず中断した

最終的な転換点: Protobuf field tagの1バイト変更

  • decrypted network trafficとProtobuf fuzzingの結果、広告は特定の動画のslotsに登録される構造だと観察された
    • slotの種類にはpre-roll、mid-roll、end-roll、full-page、ad podsが含まれる
    • 広告URLをブロックすると、「存在しない広告がslotを予約した」というようなエラーが発生し、UI panicが起きる
  • 元のschemaなしでdecode、edit、re-encodeすると、変更後のencodingになってしまい、ZigZagの使用有無やint32int64sint32/64varintのような数値型が分からず、object field orderも通常はnondeterministicなので問題になると考えられた
  • Protobufのbackward compatibilityとUnknownFieldSetの動作から回避の可能性を見いだした
    • old softwareが新しいfieldが追加されたmessageを読むと、unknown fieldが発生することがある
    • 特定のfield keyを別の値に変えれば、広告とtracking情報を含むsub-structure全体をunavailable状態にできると考えた
  • 例として、field key 4939979749399796に変え、その広告/tracking sub-structureをunknown fieldのように見せるアイデアを提示した
  • field key 49399797は単純なhex検索では見つからず、varint/tag encodingを考慮する必要があった
    • wire typeは2で、length-delimited nested string/messageを意味する
    • target field key 49399797のtag byte sequenceはAA FF B8 BC 01になる
    • 395198378 >> 3でwire type bits 3個を取り除くと、元のfield key 49399797が得られる
  • Protobuf bytes内で/pagead/のようなclassic ad URL signatureを見つけてfield search範囲を絞り、その位置から後方へ移動して変更するfield tagとfield keyを探した
  • 例のintercept logでは、youtubei.googleapis.com:443/youtubei/v1/browse?key=...へのPOSTリクエストの1.87MiB application/x-protobuf responseで、key 49399797がposition 4465、key 50195462がposition 4477で見つかった
  • O(n) smoke testでは、1.8MiBのProtobuf dataを追加メモリなしで1回だけscanした
    • targetは1.8MiB中の30,593番目のbyteで見つかった
    • 約600 bytesのbacktrackingでdenatureするfield keyを見つけた
  • この方法が機能すると、もはや*.googleadservices.com/pagead/を含むURLをブロックする必要はなくなり、そのリクエスト自体が最初から発生しなくなった

MITMProxyアドオンスクリプトの構造

  • MITMProxyアドオンスクリプトは、ネットワーク接続されたAppleデバイスでYouTube広告をブロックするproof of conceptとして提供されている
    • ファイル名はyoutube.py
    • 実行例はmitmdump --listen-port 8080 --listen-host 127.0.0.1 -s "youtube.py"
    • FreeBSDの前提条件はpkg install protobufpkg install py38-pippip install jsonpath-ngである
  • スクリプトには、コンテンツ制作者を支援するために広告を5%許可するfairness functionが含まれている
    • in_allowed_ads_window()は、現在時刻が毎時0分から2分の間であれば広告ブロックをスキップする
  • YouTubeAdBlockerはYouTube関連ドメインをinterceptし、JSONまたはProtobufレスポンスを変更して広告情報を削除する
    • intercept対象のhost regexは\.youtube\.com|google\.(com|ca)|googleapis\.com|googleadservices\.com|googlevideo\.com
    • Protobuf広告検出用文字列はb"/pagead/"
    • 検索上限は80_000バイト
    • target field tagは50195462
  • リクエスト段階のブロック一覧には、YouTubeホストのpagead/log_event?stats/adsstats/qoe?ptracking?generate_204error_204adformat=activeview?_ad_ai?sw.jsなどが含まれる
  • Web版YouTube向けのJSON replacementは、広告関連フィールドを削除または無効化する
    • yt_ad"0"に変更
    • adPlacements[]に変更
    • adPlacementRendereradPlacementConfigplayerAdParamsgutParams{}に変更
    • adVideoId""に変更
    • showCompanionshowInstreamuseGutFalseに変更
  • load() hookはHTTP/2を無効化し、anticomp=Truemode="transparent"を設定する
  • running() hookは、interceptがYouTube関連ドメインにのみ適用されるようallow_hostsを更新する
  • response() hookは、content-typeprotobufがある場合、レスポンス本文の先頭80,000バイト内で/pagead/を探す
    • 見つかった場合、TagBytes(self.target_field_tag, WIRETYPE_LENGTH_DELIMITED)でtarget tag bytesを作る
    • target_field_tag - 1のtag bytesを新しいbytesとして作る
    • /pagead/位置の手前をreverse searchしてtarget tagを探す
    • その位置のbytesをtarget_field_tag - 1に対応するbytesへ置き換える
    • 変更したProtobuf contentをflow.response.set_content(bytes(body))で戻す
  • コードコメントでは、このPoCはすでに広告の90%をブロックしているとし、別のセクションにも別のfield keyがあり、無力化すべき広告セクションが複数存在する可能性があると付け加えている

パフォーマンス、限界、対象ユーザー

  • 最終的な手法は、Protobufがschema changeに対してbackward-compatibleとなるようunknown fieldを許容する機能と、コンパクトな形式のsingle-byte edit sensitivityを利用している
  • 重要な箇所の1バイトを書き換え、深くネストされたセクションをfuture schema versionに属するように見せることで、Protobufがそれをignoreし、広告情報を除去できる
  • GoogleはiOSアプリのlayoutまで含む巨大なProtobufレスポンスを返しており、例示されたpayloadは1.8MiBである
  • payload全体をparseするにはC++/Swiftのようなnative codeが必要で、Pythonでのデコードは桁違いに遅く、接続タイムアウトを引き起こすとしている
  • WebベースのJSONはpayload全体をparse、edit、re-serializeする必要があるが、Protobuf手法はlinear scanと高速なbacktrackだけで済むため、microseconds単位で処理でき、real-time adblockingに適しておりblocklistも不要である
  • Appleデバイスのすべての*.googleadservices.comおよび/pagead/* URLはProtobuf payloadから出てくるため、payloadからad dataが消えれば、それらのrequestも自動的に消える
  • YouTubeアプリはad URLをfetchしようとしないため、より高速に感じられ、広告がvideo slotに登録されないのでcontentがすぐ再生される
  • この方式は、Appleデバイス向けYouTube広告やInstagram、WhatsApp、Facebookのtracker trafficをブロックするためのhighly specialized techniqueとして提示されている
  • HTTPS trafficをdecrypt/re-encryptするCPU要求は、Raspberry Piの性能を大きく上回るとしている
  • OSをcompromiseしたくないAppleデバイス所有者を対象としているため、利用者層はさらに狭いと見ている

YouTube Premiumと広告コスト実験

  • YouTube Premiumの価格がCAD $9.99/moまたはCAD $11.99/moで、税別ではなく税込みで約CAD $13.43/moになるとき、それが妥当かどうかは不確かだと見ている
  • クリーンなノートPCとprivate browsingで、1日のあいだ断続的にYouTubeを視聴する広告露出実験を行った
    • 視聴履歴上で「見た」動画は10本だけだった
    • 10本の動画の一部を視聴するあいだに8本の広告にさらされた
    • スキップできる広告は2本だけで、どちらもスキップした
  • おおよそのCPVをUSD $0.15と仮定すると、1日8本の広告は8 x $0.15 = $1.20の広告主コストであり、1カ月に外挿すると約USD $36/moになる
  • Statista資料に基づき、米国の広告支出を総再生回数で割る計算も示している
    • 2019年に米国の広告主はYouTubeに$15.1 billionを支出した
    • 米国居住者は916 billion本の動画を視聴したという
    • 平均は$15.1B / 916B = USD $0.0165 per viewである
    • 著者の場合、1日あたり約USD $0.13、1カ月あたり約USD $3.96の広告主コストに相当すると計算している
  • 広告実験中はハードウェアミュートにし、しばしば視線を外していたため、自分に配信された広告費は無駄だったと見ている
  • それでも制作者を支援したいとして、Googleが自分について何を追跡しているのかを監視し続けながら、3カ月のPremium trialを試すつもりだとしている
  • DMCA claimが受理された瞬間から、すべての広告収益が制作者ではなくclaimantに渡る可能性があることを懸念しており、多くの制作者がPatreonへ移るのも不思議ではないと付け加えている

最終整理

  • ハードウェアルーターを最初から設定し、LANをtrusted/untrusted zoneに分離した
  • 従来型のDNS広告ブロックを設定した
  • transparent MITM proxyを追加した
  • 最終的にネットワーク接続されたApple機器でYouTube広告を高性能にブロックできたとしている
  • 難しい部分は終わったのでYouTube Premiumの支払いを検討するとしつつも、trackerは依然として強力にブロックされると付け加えている

1件のコメント

 
GN⁺ 2025-03-19
Hacker News のコメント
  • Protobuf 形式の欠陥というより、作者がフィールド番号を大きな未使用の番号に変えたように見える
    /pagead/ のような広告 URL シグネチャを Protobuf のバイト列から探してフィールド範囲を特定し、そこから後方に移動して対象フィールドのタグとフィールドキーを見つけて無効化する、という方法だが、これは欠陥ではなく 意図された動作 に近い
    タグを見つけるだけの労力をかけるなら、すぐ隣の varint 長を読んで該当バイトをスキップするのも大した追加作業ではない。バッファをコピーするかバイトをずらす必要はあるだろうが、PoC スクリプトも mitmproxy API が返す bytes が不変なので、すでにコピーを行う必要がある

    • プロトコルのレベルでは想定どおりに動作しているが、広告データ構造で未知のフィールドが出てきたときに Google がエラーにせず、広告がないかのように処理している点が抜け穴に見える
      Google は、既存のアプリバージョンで広告がまったく出なくなるほどプロトコルを変える前に新しいアプリを先に配布するだろうから、基本的な証明書ピンニングや、広告情報の抽出失敗にあまり寛容でないデコード方法だけでも、このブロック手法はすぐに防げる。YouTube チームならこういう部分を欠陥と見ると思う
    • bytes オブジェクトは不変だが、bytearray オブジェクトは不変ではない
  • 小さな C++/Go プロキシ でも、はるかに少ないオーバーヘッドで同じことができる。こういう明確に定義された作業は、mitmproxy と格闘するより安定していて手間も少ない
    すべてのトラフィックをプロキシに通すと、SNI の傍受を使っても性能が落ちる。pfSense も同様で、単純な Linux サーバーと簡単な iptables ルールがあれば、pfSense の抽象化レイヤーと戦わずに処理できる
    リバースエンジニアリングした proto フィールドのうち必要な分だけを .proto ファイルに書いてコードを自動生成し、フラグを変えればよい。Python 実装より低コストで、proto が変わっても更新しやすい。未知のフィールドタグを無視することは Protobuf の重要な機能であり、既存のデプロイを壊さずに互換性のあるスキーマ変更を可能にしている

    • YouTube 体験をあえて遅くし、動画の切り替えももたつくようにするほうがいい場合もある。特に Shorts の中毒性はかなり下げられそう
    • その方法を詳しく共有するブログ記事を期待したい
    • 非効率がどこにあり、より単純なソフトウェアでどう緩和できるのか、ガイドを自分で書いてくれるとよい
      筆者はすでにコメントのかなりの部分を認識していたようだし、記事もかなり綿密だ。Python と C++ でベンチマークしており、最終実装では Protobuf のデコードすらしていない。複数の mitm ソリューションも試していて、pfSense は単なるセキュリティルーターではなく、VLAN と VPN で Apple TV のトラフィックだけを狙うために使っている
      このコメントは安易で見下した感じが強すぎる。元記事はそうではないのだから、そう言うならコミュニティのために自分で証明してほしい
    • macOS 上で動き、家の中の他の機器にも提供できる 軽量プロキシ のおすすめがあるのか気になる
  • YouTube Premium に課金するとクリエイターを支援することになるのだろうか? そうなら Patreon のような直接支援と比べてどの程度なのか気になる

    • Patreon と比べれば多くはないだろうが、複数の YouTuber を見ているからといって全員の Patreon を購読するよう期待するのも難しい
      YouTube Premium のサブスク 1 件が個々のクリエイターにもたらす収入は微々たるものだろうが、広告ブロック状態で動画を見るよりはましではある
    • 通常の広告視聴による再生よりも、YouTube Premium 視聴 のほうがクリエイターの取り分は大きいとされている。広告をスキップすると収益がないからだ。ただし Premium 利用者は少ないので、依然として限界はある
    • 最近の情報は少ないが、最初に Youtube Red として開始されたときは、再生あたりの広告収益より一般的にはずっと多かった
    • 広告よりは多く、Patreon よりは少ない
      広告表示回数ではなく 視聴時間 基準なので、長尺コンテンツを作るクリエイターのほうが有利
  • ガールフレンドの YouTube アカウントは奇妙なことに、どの端末でログインしても広告が出ない。Apple TV も含まれ、Premium ではなく、Premium だったこともない
    内部的に何らかのフラグが設定されて広告がオフになっているのか気になる

    • アカウントのユーザー名とメールアドレスを DM で送ってくれれば、確認して直せる
    • ガールフレンドは実質的に広告の 対照群 に入っているように思える。広告を見る人たちの行動と比較して、広告がユーザーにどんな影響を与えるかを把握するために使える
    • ホールドバック実験に入っているのかもしれない。広告配信のような機能が指標にどんな影響を与えるかを見るため、一部のユーザーをホールドバック群に置くことはよくあり、Google で働いていたときにもそうした実験をしていた
    • 昔 Google Music のサブスクがあると YouTube の広告がオフになっていた。サービスが終了したりサブスクを解約したりした後も、6か月以上 YouTube の広告は戻ってこなかった
      そのときになって初めて、人々がなぜ不満を言うのか分かった瞬間が来た
    • Twitch で同じ経験をしている
      広告ブロッカーを使っていないのに、ログインするだけでウェブサイトでもモバイルアプリでもどこでも広告が出ない。Twitch Turbo もなく、Amazon Prime ももうない。他の Turbo 特典もないので、完全に Turbo としてフラグ付けされているわけでもない
      以前バグバウンティをやっていてあれこれ触っていたときに、アカウントプロファイルが偶然壊れたのかは分からないが、この特典を維持してくれるなら、もっと詳しい情報も提供できる
      変なのは、以前病院で薬で朦朧とし、痛みの中でただテレビを見たかったのに Twitch の広告がひどすぎて、ほとんどメンタルが崩壊しそうになった記憶があることだ。それから1〜2年後、ふと何年も広告を見ていないことに気づいた
      おそらく長く忘れられた 広告なし A/B テスト があり、整理する価値がないので残っているのだろう。そのおかげで何年も得をしており、Twitch を他のどのプラットフォームよりも多く見ている。英国の Twitch Turbo は月額 £12、約 $15.50 で世界的にも高いほうで、米国や欧州の $12/€12 と比べるとかなり損な価格だ
  • Apple TV と外部インターネットの間に中間者プロキシを置けば HTTPS トラフィックを復号できるという点は、かなり驚きだった。
    普通は動作しないはずだと思っていたが、あとで Apple TV の証明書ストアに CA を追加できると知って、また別の意味で驚いた。スタック全体を見渡す綿密な記事だった。

    • Apple が証明書の追加をサポートしている理由を推測すると、企業や教育環境で Apple TV を AirPlay ボックスとして使う際に、IT やデバイス管理の要件に合わせるためである可能性が高い。
      例えば大学では、Wi-Fi にデバイスを接続するには MAC アドレスを許可リストに入れるか、証明書をインストールする必要があった。
    • Google は、YouTube アプリで SSL 証明書に署名した CA を確認するだけでも、この方法を簡単に防げる。
      ただしそうすると多くの企業環境で YouTube が壊れる可能性があるので、実際にやるかは分からない。それでも残念ながら、防ぐのは非常に簡単だ。
    • Apple TV に CA を追加できるとは予想していなかった。有効な証明書チェーンのないリソースに Apple TV でアクセスしたことがなかったので、知らなかったのだと思う。
    • ほとんどのデバイスは CA の追加を許可しているが、最近のアプリはほぼすべて証明書ピンニングを使ってシステムの証明書ストアを無視する。YouTube がそうしていないのは非常に驚きだ。
    • 皮肉なことに、Android TV は少なくとも 7.x バージョンではこれを許可していない。信頼されていない Let's Encrypt 証明書を回避しようとして苦労して知った。
  • Apple TV で何度か実装しようとしたが、まったく成功しなかった。YouTube が今ではアプリに証明書ピンニングを入れたか、そういうことなのだと思う。最近これを動かせた人がいるのか気になる。

    • 時間をかける気があるなら Frida [0] を掘ってみればよい。ピン留めされた証明書も問題ではない。
      [0] https://frida.re/docs/home/
  • 使わざるを得ないひどいオンラインサービスをネットワーク全体でブロックしようとするあらゆる試みは好ましい。
    広告ブロックもよいが、YouTube Shorts や Instagram Reels のような攻撃的な無限スクロールを、ネットワーク全体でより簡単に、より広く止める方法があればいいと思う。
    Instagram ではフォローしている人たちの投稿とストーリーだけを見たいのに、注意を奪うよう設計されたくだらない動画をおすすめされないでほしい。意志の弱さをさらしているだけかもしれないが、つい何本か見てしまい、人生の 15 分を失うことがよくある。

    • 強制されているわけではない。使わなくてもいいし、料金を払うこともできる。
      インターネット利用者は概して、料金を払いたくないほうを選んできたので、誰かがそのコストを負担している。全体として、インターネット利用者は広告を表示しない側に報酬を与えていない。コンテンツは欲しいが、たいていは無料で欲しがっている。
    • アプリを消して Web ページを使い、ユーザースクリプトを許可するブラウザを使えばよい。
      Instagram ページを単なる画像タグのように変えて、写真だけ見られるようにするスクリプトを見つけた: https://greasyfork.org/en/scripts/5014-un-instagram
    • こうした戦術は、私たちの自然な好奇心と、その周辺の美学を悪用しているのだと思う。
      だから意志が弱いというより、私たちが積み上げてきた麻痺に近いように見えるし、それはかなり悪いことだ。プラットフォームに私たちを使わせるのではなく、私たちがプラットフォームを使う状態へ戻そうとする努力と創意工夫は尊重に値する。
    • 親の立場としては特に共感する。子どもたちがアルゴリズムにはまっていくのを見るのはつらい。
      子どもたちとは定期的に話しており、子どもたちも有害だという点には同意しているが、抵抗するのがあまりに難しい。私自身でさえ、ときどき doomscrolling に引き込まれる。
      可能なところでは Pi-hole で広告フィルタリングを設定しているが、YouTube 全体をブロックしたいわけではない。それでも家族を守るには、今後は真剣に検討することになりそうだ。
    • Instagram の無限スクロールを止めるには、このアプリが非常によく合っていた: https://www.distractionfreeapps.com/index.html
  • エンジニアリングは良いが、自分のハードウェアやソフトウェアを多少なりとも所有しているかのように使うために、ここまでしなければならないのは少し悲しい。

    • この場合、デバイスは所有している。ただし YouTube やそのコンテンツまで所有していると主張する根拠はなさそうだ。
    • 30 ドルの Android ボックスに NewPipe APK を載せれば、ほぼ 10 年前から可能だったことだ。
  • YouTube に広告なんてあるのか? ブラウザがあまりにうまくブロックしてくれるので知らなかった。
    本当の問題は、Apple TV での体験が一般的な Web ブラウザでの体験よりはるかに悪いことだ。Apple がハードウェアを過度にロックダウンしているため、お金を払って購入した最終消費者よりも、YouTube の広告収益に役立つ構造になっている。

    • Linux、Windows、Android では広告をまったく見ない。たまに iPad で YouTube を見ようとすると、広告がどれほど頻繁で煩わしいかに驚かされる。
      自宅の Pi-hole ネットワークの外で iPad から Web を見るときも同じだ。人々がこれを毎日どうやって耐えているのか分からない。
      iPad は業務用に支給されたデバイスなので個人用途ではあまり使わないが、使うたびにどれほど煩わしいかを思い出す。
      不思議なことに、iPad を受け取る前はコンテンツ消費用としてだけ有用だろうと思っていたが、実際には業務リソースへ素早くリモートアクセスするのに非常に便利で、通常の Web ブラウジングとストリーミングメディアでは広告に覆われた荒野に閉じ込められたデバイスだった。
  • 広告なしの YouTube が必要なら、https://yewtu.be や他の Invidious インスタンス https://docs.invidious.io/instances/ を使えばよい。
    YouTube と Invidious の間には軍拡競争があり、ときどき Invidious が動作しないこともあるが、チームは常に YouTube を回避して広告なしで動画を届ける新しい方法を見つけてきた。

    • タイトルに “on AppleTV” と入っているのには理由がある。代替クライアントやフロントエンドはそこで動作しない。
    • Roku TV のようなところではブラウザがないので、この方法は通用しない。