LoginSignup
7
8

SafariでサードパーティCookieを取得するStorage Access APIを試してみた

Last updated at Posted at 2020-03-27

概要

iOS13.4のSafariにおいて、サードパーティCookieが完全にブロックされるようになりました。
詳しくは下記をご確認ください。
https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/

iOSの 設定>Safari>サイト越えトラッキングを防ぐ をOFFにすると、サードパーティCookieの読み書きが可能になりますが、Safariのデフォルトの設定では、サイト越えトラッキングがONとなっており、サードパーティCookieはブロックされます。

ここでは、上記のwebkit.orgでサードパーティCookieを取得する方法(Option 2)として挙げられている Storage Access API を試してみます。
結論から言いますと、サイト越えトラッキングがONの状態で、サードパーティCookieを取得することが出来ました。(document.cookieのみだが)
2020年3月27日時点のiOS13.4 のSafariで検証しています。

サンプルコード

ファイルを下記のように配置してください。
hoge.com
└parent.php
foo.com
├child.php
└first_party_cookie.php

parent.php

iframe タグを設置するページになります。

parent.php
<?php
setcookie("1st-party-from-server", "hoge.com", time() + 86400);
?>
<!DOCTYPE html>
<meta name="viewport" content="width=device-width,initial-scale=1">
<html>
  <head>
    <meta charset="UTF-8" />
    <title>iframe test</title>
  </head>
  <body>
    <div>parent.php</div>
    <ul>
      <li><a href="http://foo.com/first_party_cookie.php">first party cookie set in foo.com</a></li>
    </ul>
    <div>
        <iframe src="http://foo.com/child.php" width="100%" height="400">
        </iframe>
    </div>
  </body>
</html>

child.php

parent.php のiframeのsrcに設置するページになります。

child.php
<?php
setcookie("3rd-party-from-server", "foo.com", time() + 86400);
?>
<!DOCTYPE html>
<meta name="viewport" content="width=device-width,initial-scale=1">
<html>
  <head>
    <meta charset="UTF-8" />
    <title>iframe test</title>
  </head>
  <body>
    <div>child.php</div>
    <div>$_COOKIE:<pre><?=htmlspecialchars(print_r($_COOKIE, true), ENT_QUOTES)?></pre></div>
    <div><button onclick="callRequestStorageAccess()">callRequestStorageAccess</button></div>
    <div><button onclick="getCookieByUserAction()">getCookieByUserAction</button></div>
    <script>
        window.onload = function() {
            console.log("Cookie when loading:" + document.cookie);
 
            var promise = document.hasStorageAccess();
            promise.then(
                function (hasAccess) {
                    // Boolean hasAccess says whether the document has access or not.
                    console.log("hasStorageAccess when loading:" + hasAccess);
                }
            );
        }
 
        function callRequestStorageAccess() {
            var promise = document.requestStorageAccess();
            promise.then(
                function () {
                    console.log("Storage access was granted");
                },
                function () {
                    console.log("Storage access was denied");
                }
            );
        }
 
        function getCookieByUserAction() {
            console.log("Cookie when user action:" + document.cookie);
            var promise = document.hasStorageAccess();
            promise.then(
                function (hasAccess) {
                    // Boolean hasAccess says whether the document has access or not.
                    console.log("hasStorageAccess when user action:" + hasAccess);
                }
            );
        }
    </script>
  </body>
</html>

first_party_cookie.php

foo.com のファーストパーティCookieをSetするために用意します。iframe内ではなくTOPフレームとして遷移する必要があります。

first_party_cookie.php
<?php
setcookie("1st-party-from-server", "foo.com", time() + 86400);
?>
<!DOCTYPE html>
<meta name="viewport" content="width=device-width,initial-scale=1">
<html>
  <head>
    <meta charset="UTF-8" />
    <title>first-paty-cookie</title>
  </head>
  <body>
    <div>first_party_cookie.php</div>
    <div>
        $_COOKIE:<pre><?=htmlspecialchars(print_r($_COOKIE, true), ENT_QUOTES)?></pre>
    </div>
    <div><button onclick="history.back()">back</button></div> 
    <script>
        document.cookie = "1st-party-from-js=foo.com";
    </script>
    </body>
</html>

確認方法

実機のiPhoneとMacを接続して行いました。コンソールについてはMacのSafariのWebインスペクタを使用しました。
SafariのWebインスペクタの使用方法はここでは割愛します。

  1. iPhoneのSafariにて、hoge.comの parent.php を開きます
    parent.png

  2. first_party_cookie in foo.com のリンクを押して、foo.comに設置した、first_party_cookie.php へ遷移します。
    リロードをして、Cookieがセットされたのを確認してください。
    first_party_cookie.png
    Safariの特徴として、foo.comのファーストパーティCookieが存在しない場合、後に実行するrequestStorageAccess()が失敗してしまいます。そのため事前にSet-Cookieしています。
    backボタンで戻ります。

  3. parent.phpをリロードします。
    コンソールに下記のように表示されます。
    console_copy1.png
    Cookie when loading:の後ろに何もないので、ロード時のdocument.cookieがnullになっていることが確認できます。
    先ほど確認した1st-party-from-serverなどのCookieが、サードパーティではブロックされているためです。
    まだ、requestStorageAccess()をしていないため、hasStorageAccessはfalseとなっています。

  4. callRequestStorageAccessボタンを押します。
    下記のポップアップが表示されます。
    popup.png

許可を押し、requestStorageAccess()が成功した場合はコンソールにStorage access was grantedと表示されます。
console_copy2.png

  1. getCookieByUserActionボタンを押します。
    コンソールにfoo.comのfirst_party_cookie.phpでSetしたCookieが表示されます。
    これでサードパーティのdocument.cookieが取得できました。
    hasStorageAccess()の結果もtrueになりました。
    console_copy3.png

注意点

  • 事前にファーストパーティでSet-Cookieする必要がある。
  • ユーザジェスチャーで、requestStorageAccess()をcallする必要がある。
    • requestStorageAccess()をpromiseのcallbackのfunctionに配置した場合、ユーザジェスチャとみなされず、失敗してしまうので注意。
  • ページをリロードした場合は、requestStorageAccess()で取得した許可がリセットされてしまう。
    • その場合は再度ユーザジェスチャによるrequestStorageAccess()をcallする必要がある。
    • 2回目はポップアップは表示されない。

まとめ

めでたくdocument.cookieは取得できたのですが、child.php への通信を覗いてみたたところ、Cookieヘッダーは存在しませんでした。
そのためサーバ側での$_COOKIE での取得は不可能でした。
現在の実装がサーバ側でのCookie取得に依存している場合は、document.cookieへの移行はハードルが高そうです。
またユーザジェスチャーが必要である点、リロードするとリセットされる点は、移行を不可能にする要素になるかもしれません。
2024年時点では、CHIPSと組み合わせることで永続的なログインが可能になっています。下記の記事を参照してください。
https://qiita.com/reoito/items/357d0e8e63290200f0fe

7
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8