NO AD NO LIFE

ベンチャー企業の広告エンジニアのブログ

ITP 2.0の機能の検証

はじめに

ITP(Inteligent Tracking Prevention)はブラウザ(Safari)上のユーザ行動の追跡を抑止する機能で、2017年に発表された最初のバージョンであるITP1.0は広告業界を震撼させました。 そして、2018年6月4日に新しいバージョンであるITP2.0が発表されました。ITP2.0ではさらに厳しいユーザ行動の追跡抑止機能が追加されています。

ITP2.0の主な機能として、

  • トラッカー(ユーザ行動の追跡者)により付与された3rd Party Cookieの即時的な排除
  • Storage Access APIを利用したCookieの管理
  • ファーストパーティバウンストラッカーに対する保護
  • リファラヘッダからドメイン以外部分の排除

があります。*1

今回はこれらの機能について、トラッキング環境を擬似的に構築し、動作検証を行なっていきます。

検証環境

今回想定するトラッキング環境

ユーザのトラッキングにはいくつかの方法がありますが、今回はトラッキングサーバのリダイレクトを利用した形式を想定します。

f:id:inchom:20180617005859p:plain
リダイレクタを利用したトラッキング

ユーザが広告をクリックすると、一度広告ベンダの計測サーバへリダイレクトされ、そこで広告ベンダのドメインへのCookieの付与が行われます。 その後、ユーザが購買ページへ到達すると、計測タグが発火してリダイレクト時に付与されたCookieを読み込み、広告経由のユーザかどうかを判定するという流れになります。

環境構築

想定する環境をローカルのPC上に作っていきます。本論だけ読みたい場合は読み飛ばしても大丈夫です。 ここで紹介するコードについてはこちらGitHub - satoshi03/ITP2-Investigation: For ITP2.0 Investigationソースコードがありますので、気になる人は参照してください。

仮想的なドメインの設定

/etc/hostsを編集して、仮想的にドメインを作ります。

# for ITP2.0 test
127.0.0.1       www.media1.com
127.0.0.1       www.media2.com
127.0.0.1       www.media3.com
127.0.0.1       www.media4.com
127.0.0.1       www.ad-vendor.com
127.0.0.1       www.advertiser.com

メディアページ

メディアページには、広告が貼られているという想定で、クリックするとリダイレクタへ遷移するリンクが貼られています。

<html>
  <body>
    <h1>Media Page</h1>
    <a href='http://www.ad-vendor.com:8888/redirect?to=http://www.advertiser.com/advertiser/lp.html' target='_self'>Click to redirect</a>
  </body>
</html>

今回はpython3を利用して簡易的なWebサーバを80番ポートで立ち上げています。

$ sudo python -m http.server 80

広告ベンダトラッキングサーバ

メディアの広告から遷移してきたユーザにCookieを付与し、遷移先の広告主サイトのランディングページへリダイレクトします。

package main

import (
        "fmt"
        "log"
        "time"

        "github.com/buaazp/fasthttprouter"
        "github.com/pborman/uuid"
        "github.com/valyala/fasthttp"
)

func redirectHandler(c *fasthttp.RequestCtx) {
        // Get referer
        referer := c.Referer()
        log.Printf("redirect from %s", string(referer))

        // Set tracking cookie
        cookie := fasthttp.Cookie{}
        cookie.SetKey("ADUID")
        cookie.SetValue(uuid.NewRandom().String())
        cookie.SetExpire(time.Now().AddDate(1, 0, 0))
        cookie.SetDomain("ad-vendor.com")
        cookie.SetPath("/")
        c.Response.Header.SetCookie(&cookie)

        // Redirect to designated page
        redirectTo := string(c.QueryArgs().Peek("to"))
        if redirectTo != "" {
                log.Printf("redirect to %s", redirectTo)
                c.Redirect(redirectTo, 302) //use 302 to avoid browser cache
        } else {
                fmt.Fprintf(c, "error. not redirect")
                c.SetStatusCode(fasthttp.StatusNotFound)
        }
}

func retargetHandler(c *fasthttp.RequestCtx) {
        // Get referer
        referer := c.Referer()
        log.Printf("user views: %s ", string(referer))
        c.SetStatusCode(fasthttp.StatusOK)
}

func main() {
        router := fasthttprouter.New()

        router.GET("/redirect", redirectHandler)
        router.GET("/retarget", retargetHandler)

        log.Printf("Start HTTP server")
        panic(fasthttp.ListenAndServe(":8888", router.Handler))
}

8888番ポートでWebサーバを立ち上げています。

$ go run server.go

広告主ページ

広告主ページは、ランディングページと購買ページのそれぞれを作成しています。

ランディンページ

ランディングページには、購買ページへのリンクが貼られています。

<html>
  <body>
    <h1>Advertiser Landing Page</h1>
    <a href="http://www.advertiser.com/advertiser/cvp.html">Go to conversion page</a>
    <iframe height="0" width="0" frameBorder="0" src="http://www.ad-vendor.com:8888/retarget"></iframe>
  </body>
</html>

購買ページ

購買ページでは、iframeで広告ベンダが提供するcv用のタグを呼び出しています。

<html>
  <body>
    <h1>Advertiser Conversion Page</h1>
    <iframe src="http://www.ad-vendor.com/ad-vendor/cv.html"></iframe>
  </body>
</html>

メディアと同様にpythonのwebサーバ上で動作します。

計測タグ(広告ベンダ)

計測タグは、広告ベンダのドメインの3rd Party Cookieを読み込み、検証のためにページ上に表示するようにしています。

<html>
  <body>
    <p id="tracking-cookie"></p>
    <script src="http://www.ad-vendor.com/ad-vendor/cv-tag.js"></script>
  </body>
</html>
function getCookie(cname) {
  var name = cname + "=";
  var decodedCookie = decodeURIComponent(document.cookie);
  var ca = decodedCookie.split(';');
  console.log(document.cookie);
  console.log(ca);
  for(var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) == ' ') {
      c = c.substring(1);
    }
    if (c.indexOf(name) == 0) {
      return c.substring(name.length, c.length);
    }
    return "";
  }
}

var uid = getCookie("ADUID");
var el = document.getElementById("tracking-cookie");
console.log(uid);
el.innerHTML = "tracking cookie uid:" + uid;

検証

ITP2.0の検証を行っていきます。この検証では主に従来のバージョンのITPとの差分に着目し、以下の特徴の検証を行います。

  • トラッカーの判定の改善
  • トラッカーにより付与された3rd Party Cookieの即時的な排除
  • リファラヘッダからドメイン以外の情報の排除

Storage Access APIに関しては、現在(2018/6/16)調査中です。詳細が判明し次第更新したいと思います。

検証1. トラッカーの判定の改善

TP 2.0でどのように同様にトラッカーの判定がなされているのかを検証してみます。

ITPでは、ユーザが訪問したサイトに埋め込まれたフレームや、そのサイトからロードされたタグなどのリソースを監視・収集し、トラッカーとしての機能を持つかどうかを分類することで、トラッカー判定を行います。 ITP2.0では、リソースアクセスを行わないようなリダイレクタによる追跡も対象になるなど、さらにトラッカーの判定が厳しくなっています。

トラッカーの判定

ITPでは、トラッカーとして判定する際には以下の要素を利用しているようです。*2

最後の要素に関しては、公式のブログで明言されている訳ではありませんが、ITP2.0から追加された要素で、下の図のようにタグやフレームの埋め込みを利用せず純粋なナビゲーションリダイレクトのみを利用してユーザを追跡をする場合にトラッカーと判定するために利用されていると思われます。

f:id:inchom:20180617002812p:plain

これらの要素のドメインの数が4以上となるとトラッカーとして判定されるようです。

検証手順

検証の手順としては、一般的な広告のトラッキングの流れで行います。

  1. メディアのページ(http://www.media1.com/media/)にアクセス
  2. 広告リンクをクリックして広告ベンダトラッキングサーバへ遷移(この時のCookieを付与)
  3. ラッキングサーバが広告主ページのランディングページ(http://www.advertiser.com/advertiser/lp.html)へリダイレクト
  4. 購買ページ(http://www.advertiser.com/advertiser/cv.html)へのリンクをクリック
  5. 購買ページからCVタグをロードしてCookieが参照できているかを確認
  6. メディアを変更してトラッカーと判定されるまで1-5を繰り返す

f:id:inchom:20180617005758p:plain
ITP2.0によるCookieのブロック

余談ですが、今回のケースはiframeやタグなどのリソースを貼っていないリダイレクト元ドメイン(メディア)を変更していくことになります。そのため、ITP1.1ではドメイン数が増えずトラッカーとして判定されないことは確認済みです。

メディア1(www.media1.com)

まずは、1から5までの手順を行います。

対象のドメインは、

  • www.media1.com
  • www.advertiser.com

となり、ドメイン数が2となるため、まだトラッカーとしては判定されていないようです。

f:id:inchom:20180617001244p:plain

メディア2(www.media2.com)

メディアをwww.media2.comに変更して手順6を行います。

  • www.media1.com
  • www.media2.com
  • www.advertiser.com

ドメイン数が3となるため、まだトラッカーとしては判定されていないようです。

f:id:inchom:20180617001710p:plain

メディア3(www.media3.com)

メディアをwww.media3.comに変更して手順6を行います。

  • www.media1.com
  • www.media2.com
  • www.media3.com
  • www.advertiser.com

この段階ではCookieを参照することができていますが、ドメイン数が4となるため、これ以降トラッカーとして判定されると思われます。

f:id:inchom:20180617001320p:plain

メディア4(www.media4.com)

メディアをwww.media4.comに変更して手順6を行います。

トラッカーとして判定されているため、Cookieを参照することができなくなりました。

f:id:inchom:20180617001515p:plain

この結果から、ITP2.0では判定に利用する要素のドメイン数が4以上となったときにトラッカーと判定され、純粋なナビゲーションリダイレクトも含まれるようです。

検証2.トラッカーにより付与された3rd Party Cookieの即時的な排除

ITP 1.1では、ユーザがトラッカーのドメインのページで何らかのインタラクション(クリックやテキスト入力など)を行った場合は、そのインタラクションから24時間は3rd Party Cookieの付与を許可するようになっていました。*3

f:id:inchom:20180617005441p:plain
ITP1.1でのユーザインタラクションによる3rd Party Cookieの一時的な許可

ITP 2.0では、ユーザがトラッカーのドメインのページで何らかのインタラクションを行った場合であっても、即時的に3rd Party Cookieを排除するように変更されています。

f:id:inchom:20180617005529p:plain
ITP2.0ではユーザインタラクション後も3rd Party Cookieを排除

ユーザインタラクション後も3rd Party Cookieのブロックされるか検証を行っていきます。

先ほどの検証でトラッカーとして判定された www.ad-vendor.com というドメインのページを用意します。

  • 広告ベンダインタラクションページ
<html>
  <body>
    <h1>Ad Vendor Page</h1>
    <form action="/">
      <input type="text" name="hoge"><br>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

インタラクション(テキスト入力とクリック)を行います。

f:id:inchom:20180616033848p:plain

その後、メディアページに戻って、同様の手順で購買ページへ行ってみます。 やはりCookieを取得できていません。*4

f:id:inchom:20180616223817p:plain

仕様と同様にITP2.0では、インタラクションを行った後でもCookieが即時的に排除されることが確認されました。

検証3. リファラヘッダにドメイン以外の情報を排除

ITP2.0 ではドメインがトラッカーとして判定された場合、リファラヘッダからドメイン以外の部分が排除されるようになりました。 これにより広告主ページ上にリターゲット用のタグなどを貼っていても、どのユーザがどのページを踏んだかを判定できなくなる可能性があります。

f:id:inchom:20180616032433p:plain
ITP2.0によるリファラからドメイン以外の部分の排除

実際に、広告主ページ上にiframeでリターゲット用のiframeを置き、サーバ側でリファラがどのような形で取得されるかを検証します。

  • 広告主ランディングページ
<html>
  <body>
    <h1>Advertiser Landing Page</h1>
    <a href="http://www.advertiser.com/advertiser/cvp.html">Go to conversion page</a>
    <iframe height="0" width="0" frameBorder="0" src="http://www.ad-vendor.com:8888/retarget"></iframe>
  </body>
</html>

手順は、検証1で行ったやり方と同様に、トラッカーと認定されるまでリダイレクトを繰り返します。 トラッカーとして判定された時に、サーバ側に送られる情報を調べます。

結果としては、トラッカーとして判定された瞬間から、リファラヘッダからドメイン名以外の部分を排除することが確認されました。

f:id:inchom:20180616031239p:plain
取得可能なリファラ

この部分に関しては、サーバ側に送られるリファラヘッダが削られるだけであるため、リクエスト内のURLパラメータにリファラを含めて送信することで回避可能かと思います。

おわりに

今回は、6月4日に公開されたITP2.0について、調査・検証を行いました。

結果として、ITP2.0では以下のものが更新されていることが分かりました。

  • 判定に利用する要素ドメイン数が4以上となったときにトラッカーと判定され、純粋なナビゲーションリダイレクトも含まれる
  • ユーザがトラッカーのドメインのページで何らかのインタラクションを行った場合であっても即時的に3rd Party Cookieを排除する
  • トラッカーとして判定された瞬間からリファラヘッダからドメイン名以外の部分を排除する

所感ですが、ITP2.0ではより3rd Party Cookieに対する制約が厳しくなったものの、これまでに1st Party Cookieを利用した形式に計測を変更しているベンダーの場合は、それほど影響が大きくないのではないかと感じました。

それよりもあまりクローズアップされていないですが、リファラヘッダからドメイン以外の部分を排除する修正は、気づかない内に配信ボリュームに影響を与える可能性があるため注意が必要かと思います。

Storage Access APIに関しては現在調査中ですが、詳細が判明し次第アップデートをする予定です。

今回の検証内容に関するコメントや質問がありましたら、下のコメント欄に書いていただけたらと思います。

*1:機能のより詳細な情報については、Intelligent Tracking Prevention 2.0 | WebKitを参照してください。

*2:ITPの仕様と挙動について、あまり知られていないことを簡単に整理する – マーケティングメトリックス研究所/MARKETING METRICS Lab.のサイトを参考にさせて頂きました

*3:ここでのインタラクションはページのリダイレクトや表示は含まれません。

*4:ITP1.1では同様の手順でCookieを取得できるようになることを確認済みです。