作って学ぶファーストパーティー製クッキー

7 min read読了の目安(約6800字

アドテクを学習する上で、避けられないクッキーについて学習しながら簡単に実装してみました。

今回はファーストパーティー製クッキーのセット方法の実装に重点をおいております。セキュリティ上の懸念点等は記載できておりませんので、むしろコメントで補足・追記してくださると助かります。

そもそもファーストパーティーとかサードパーティーとは

こちらの記事が非常に参考になります!
本記事もこちらに記載されている内容を参考に記載させていただいております。本当にありがとうございます。

https://www.nexal.co.jp/blogs/2061.html

こちらの記事によりますと、両者の定義は以下とのこと。

両者の定義は非常にシンプルで、”ユーザがブラウザでアクセスしているドメインが発行しているCookie”なのか(=ファーストパーティCookie)、それとも”第三者ドメインが発行しているCookie”なのか(=サードパーティCookie)という違いしかありません。

こちらの記事にかなり詳しく例を書いてくださっていますが、今回はファーストパーティー製クッキーについて実装しながら理解を深めたいと思います。

サードパーティー製クッキーは何がまずいのか

Webシステムでは欠かせないものとして使われているクッキーですが、プライバシーの観点からサードパーティー製クッキーの排除が進んでいます。

以下の参考記事をご参照意ください。

https://japan.cnet.com/article/35156564/

ファーストパーティCookieは、Cookieが発行されたそのドメインでしか使わないのに対し、サードパーティCookieは、ドメインをまたいで、ブラウザ側に保存された情報を活用する。サードパーティCookieは、シングルサインオンや広告におけるコンバージョンの紐づけやターゲティング、ウェブ解析としてのトラッキングなどに多く用いられている。

商品をサイトで見たり、購入したりすると広告として表示されたり(リターゲティング)する経験ございますよね?

「サイト利用者の利便性向上のために利用されるためのファーストパーティー製クッキーなら良いが、ユーザーが望んでもいないターゲティングに使われるのは本来の趣旨ではないだろう」というような論調が広まるのも無理はないことかと思います・・・

GoogleAnaliticsでも、JSONPを活用したファーストパーティー製クッキーを利用するなどの対応をとっています。

JSONPの実装方法については、あまりピンとこられていない方は参考までにご参照いただければと思います。

https://zenn.dev/bun913/articles/0c807fa8da262e

JSONPを利用したファーストパーティー製クッキーのセット

これより下ではサーバーサイド(クライアント側にクッキーの情報を渡す側)の実装を Go言語のechoフレームワークを利用しています。

まず普通にサードパーティー製クッキーのセットを実装しようとする場合

まず、普通にクライアントからxmlHTTPRequest(axiosやajaxを使った非同期通信と理解してください)を送って、サードパーティー製クッキーをサーバー側にセットしてもらいます。

まずはルーティング等の設定を行います。

package main

import (
	"net/http"
	"work/controller"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func main() {
	e := echo.New()
	// e.Use(middleware.CORS())と同様
	// AllowOriginsを * とすることで異なるドメインからの通信を許可する
	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowOrigins:     []string{"*"},
		AllowCredentials: true,
		AllowMethods:     []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
	}))
	// 3rdパーティークッキーのテスト
	e.GET("/cookie/third", controller.SetCookieHandler)
	// 1stパーテークッキーのテスト
	e.GET("/cookie/first", controller.SetFirstCookieHandler)
	e.Logger.Fatal(e.Start(":3000"))
}

SetCookieHandler で 3rdパーティーのクッキーをセットする処理を記載します。

package controller

import (
	"net/http"
	"time"

	"github.com/k0kubun/pp"
	"github.com/labstack/echo"
)

type CokkieResponse struct {
	Name    string `json:"name"`
	Value   string `json:"value"`
	Expires string `json:"expires"`
}

// SetCookieHandler XMLHTTPRequestに対してクッキーをセット
func SetCookieHandler(c echo.Context) error {
	cookie := new(http.Cookie)
	cookie.Name = "third_party"
	cookie.Value = "this is 3rdParty Cookie"
	cookie.Path = "/"
	cookie.Expires = time.Now().Add(1 * time.Hour)
	c.SetCookie(cookie)
	return c.JSON(http.StatusOK, BaseResponse{Result: "ok"})
}

これだけですね。

次にクライントサイドで、ページ読み込み時に
http://サーバーサイドのIP:3000/cookie/third にxmlHTTPRequestでアクセスするコードを記載します。

<script>
window.onload = async function (_event) {
  var xhr = new XMLHttpRequest()
  xhr.withCredentials = true //クロスドメインでのクッキーセットを許可
  xhr.open('GET', 'http://[サーバーのIP]:3000/cookie/third')
  xhr.onload = function (e) {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        console.log(xhr.responseText)
      } else {
        console.error(xhr.statusText)
      }
    }
  }
  xhr.onerror = function (e) {
    console.error(xhr.statusText)
  }
  xhr.send(null)
}

// スクリプトタグを追加関数(任意のタイミングで呼び出し)
async function appendScriptTag (src) {
  let script = document.createElement('script')
  script.type = 'text/javascript'
  script.src = src
  document.body.appendChild(script)
}
</script>

が・・・・

このままではクッキーはセットされません。

chromeのデベロッパーツールを確認してみます。

デベロッパーツールのイメージ

何か警告マークが出ていますね・・・

ブラウザのデフォルトの設定では、サードパーティー製クッキーを受け入れていなかったりします。
なので、このままではユーザーの環境次第ではクッキーがセットされないという事態になってしまいます。

https://cg-support.isr.co.jp/hc/ja/articles/360000951893-ブラウザーがCookieを保持する設定になっているかを確認する

クライアントサイドでクッキーをセットしてもらうなどの工夫が必要になりますね・・・

ファーストパーティー製クッキーのセット

次にファーストパーティー製クッキーのセットを実装してみます。

まずサーバーサイドで http://[サーバーホストIP]:3000/cookie/` にアクセスが有った場合の処理を追加します。

// SetFirstCookieHandler JSONPでクッキーにセットしてほしい内容を関数にラップして返却
func SetFirstCookieHandler(c echo.Context) error {
	callback := c.QueryParam("callback")
	body := new(CokkieResponse)
	body.Name = "first_pirty"
	body.Value = "this is 1stParty Cookie"
	body.Expires = ""
	return c.JSONP(http.StatusOK, callback, body)
}

今回の実装の場合では、 setCookieメソッドは利用しません。
サーバーサイドでクッキーをセットするのではなく、セットしてほしい内容をクライアントサイドに返却し、クライアント側でクッキーにセットしてもらうためです

次にクライアントサイドでは以下のように実装します。

<script>
// ページ読み込み時点でスクリプトタグを動的に追加しJSONPによる通信を行う
window.onload = async function (_event) {
  var cookieVal = extractCookie('first_pirty')
  //既にクッキーがセットされている場合URLパラメーターに含めて送信する
  await appendScriptTag(
    'http://192.168.0.100:3000/cookie/first?callback=callBack' +
     '&setedCookie=' + cookieVal
  )
}

// callバック関数。サーバーからデータを受け取ってクッキーにセットする
window.callBack = function (jsonData) {
  if (jsonData.name) {
    const cookie = `${jsonData.name}=${jsonData.value}`
    document.cookie = cookie
  }
}

// スクリプトタグを追加する関数(任意のタイミングで呼び出し)
async function appendScriptTag (src) {
  let script = document.createElement('script')
  script.type = 'text/javascript'
  script.src = src
  document.body.appendChild(script)
}

// 設定されているクッキーから特定の値を取得
function extractCookie (key) {
  const cookiesArray = document.cookie.split(';')
  let value = ''
  for (var c of cookiesArray) {
    var cArray = c.split('=')
    if (cArray[0] === key) {
      value = cArray[1]
    }
  }
  return value
}

</script>

extractCoookie は既にセットされているクッキーから特定の値を抽出します。

  1. ページ読み込み時にJSONPによりサーバーサイドにアクセス(既にファーストパーティー製クッキーがセットされている場合は、URLパラメーターに情報をセット)
  2. サーバーサイドでクッキーにセットしてほしい内容をラップし、JSONPレスポンス
  3. クライアントサイドで、コールバック関数内でクッキーをセットする処理を記載する

という手順で あくまでクライアントサイドでクッキーをセットすることで、同一ドメインでセットされたクッキーとして取り扱ってもらいます。

デベロッパーツールで確認すると、以下のように
サーバー側でセットされたクッキーではなく、クライント側(同一ドメイン)で発行されたクッキーとしてセットされています。(jsでセットしたので当たり前ですが)

クッキーがセットされているイメージ

そして、クッキーはあくまで同一ドメインの場合にのみ HTTP リクエストヘッダーに含まれて送信されるので、JSONPによる通信の際にURLパラメーターとしてセットしています。(以下の部分)

var cookieVal = extractCookie('first_pirty')
//既にクッキーがセットされている場合URLパラメーターに含めて送信する
await appendScriptTag(
'http://192.168.0.100:3000/cookie/first?callback=callBack' +
'&setedCookie=' + cookieVal
)

https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies#Define_where_cookies_are_sent

まとめ

以上超簡単に実装いたしましたが、実際は既存のクッキーがサーバーサイドに送られた場合、サーバーでの処理内容を制御する処理が追加されたりするかと思います。

脱クッキーによる広告配信の仕組みができていっていますが、クッキーについて基礎として抑えておくことでそのような技術の理解促進に繋がると思いますので、今後も積極的に学習を進めていきたいと思います。

参考

https://corp.logly.co.jp/20190510/706