概要
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 タグを設置するページになります。
<?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に設置するページになります。
<?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フレームとして遷移する必要があります。
<?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インスペクタの使用方法はここでは割愛します。
-
first_party_cookie in foo.com のリンクを押して、foo.comに設置した、first_party_cookie.php へ遷移します。
リロードをして、Cookieがセットされたのを確認してください。
Safariの特徴として、foo.comのファーストパーティCookieが存在しない場合、後に実行するrequestStorageAccess()が失敗してしまいます。そのため事前にSet-Cookieしています。
backボタンで戻ります。 -
parent.phpをリロードします。
コンソールに下記のように表示されます。
Cookie when loading:の後ろに何もないので、ロード時のdocument.cookieがnullになっていることが確認できます。
先ほど確認した1st-party-from-serverなどのCookieが、サードパーティではブロックされているためです。
まだ、requestStorageAccess()をしていないため、hasStorageAccessはfalseとなっています。
許可を押し、requestStorageAccess()が成功した場合はコンソールにStorage access was grantedと表示されます。
- getCookieByUserActionボタンを押します。
コンソールにfoo.comのfirst_party_cookie.phpでSetしたCookieが表示されます。
これでサードパーティのdocument.cookieが取得できました。
hasStorageAccess()の結果もtrueになりました。
注意点
- 事前にファーストパーティで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