检查是否启用了第三方 cookie

我有一个应用程序,需要检查客户端浏览器是否启用了第三方 Cookie。有人知道如何在 JavaScript 中实现这一点吗?

75554 次浏览

In theory you'd just have a page call out somewhere that would set a thirdparty cookie and then check for that cookie's existence. However, standard browser security does not allow scripts from domain A to do anything with cookies set on domains B,C,etc... e.g. you can't access "foreign" cookies.

如果你有一些特定的用法,比如检查广告是否被阻止(这也会阻止第三方跟踪 cookie) ,你可以检查广告服务器的内容是否在页面的 DOM 中,但是你不能看到 cookie 是否在那里。

技术背景

第三方通过 HTTP 设置和读取 cookie (不是在 JavaScript 中)。

因此,我们需要向外部域发出两个请求,以测试是否启用了第三方 cookie:

  1. 第三方设置 cookie 的地方
  2. 第二种,根据浏览器是否在第二个请求中将 cookie 发送回同一第三方,响应不同。

由于 DOM 安全模型,我们不能使用 XMLHTTPRequest (Ajax)。

显然,您不能同时加载两个脚本,或者第二个请求可能被设置为 之前,第一个请求的响应使其返回,并且测试 cookie 将不会被设置。

代码示例

给出:

  1. .html文件位于一个域上,并且

  2. .js.php文件在另一个域名上,我们有:

HTML 测试页

保存为 third-party-cookies.html

<!DOCTYPE html>
<html>
<head id="head">
<meta charset=utf-8 />
<title>Test if Third-Party Cookies are Enabled</title>
<style type="text/css">
body {
color: black;
background: white none;
}
.error {
color: #c00;
}
.loading {
color: #888;
}
.hidden {
display: none;
}
</style>
<script type="text/javascript">
window._3rd_party_test_step1_loaded = function(){
// At this point, a third-party domain has now attempted to set a cookie (if all went to plan!)
var step2Url = 'http://third-party.example.com/step2.js.php',
resultsEl = document.getElementById('3rd_party_cookie_test_results'),
step2El = document.createElement('script');


// Update loading / results message
resultsEl.innerHTML = 'Stage one complete, loading stage 2&hellip;';
// And load the second part of the test (reading the cookie)
step2El.setAttribute('src', step2Url);
resultsEl.appendChild(step2El);
}
window._3rd_party_test_step2_loaded = function(cookieSuccess){
var resultsEl = document.getElementById('3rd_party_cookie_test_results'),
errorEl = document.getElementById('3rd_party_cookie_test_error');
// Show message
resultsEl.innerHTML = (cookieSuccess ? 'Third party cookies are <b>functioning</b> in your browser.' : 'Third party cookies appear to be <b>disabled</b>.');


// Done, so remove loading class
resultsEl.className = resultsEl.className.replace(/\bloading\b/,' ');
// And remove error message
errorEl.className = 'hidden';
}
</script>
</head>
<body id="thebody">


<h1>Test if Third-Party Cookies are Enabled</h1>


<p id="3rd_party_cookie_test_results" class='loading'>Testing&hellip;</p>
<p id="3rd_party_cookie_test_error" class="error hidden">(If this message persists, the test could not be completed; we could not reach the third-party to test, or another error occurred.)</p>


<script type="text/javascript">
window.setTimeout(function(){
var errorEl = document.getElementById('3rd_party_cookie_test_error');
if(errorEl.className.match(/\berror\b/)) {
// Show error message
errorEl.className = errorEl.className.replace(/\bhidden\b/,' ');
} else {
}
}, 7*1000); // 7 sec timeout
</script>
<script type="text/javascript" src="http://third-party.example.com/step1.js.php"></script>
</body>
</html>

第一个第三方 JavaScript 文件

保存为 step1.js.php

这是用 PHP 编写的,所以我们可以在加载文件时设置 cookie。(当然,它可以用任何语言编写,甚至可以在服务器配置文件中完成。)

<?php
header('Content-Type: application/javascript; charset=UTF-8');
// Set test cookie
setcookie('third_party_c_t', 'hey there!', time() + 3600*24*2);
?>
window._3rd_party_test_step1_loaded();

第二个第三方 JavaScript 文件

保存为 step2.js.php

这是用 PHP 编写的,所以我们可以在响应之前读取 Cookie,服务器端。我们还清除了 cookie,以便重复测试(如果你想搞乱浏览器设置并重试)。

<?php
header('Content-Type: application/javascript; charset=UTF-8');
// Read test cookie, if there
$cookie_received = (isset($_COOKIE['third_party_c_t']) && $_COOKIE['third_party_c_t'] == 'hey there!');
// And clear it so the user can test it again
setcookie('third_party_c_t', '', time() - 3600*24);
?>
window._3rd_party_test_step2_loaded(<?php echo ($cookie_received ? 'true' : 'false'); ?>);

最后一行使用三元运算符输出文本 Javascript truefalse,具体取决于是否存在测试 cookie。

在这里测试。

可在 https://alanhogan.github.io/web-experiments/3rd/third-party-cookies.html为您的测试乐趣。

(作为最后的注意事项ーー 不要使用别人的服务器在未经第三方允许的情况下测试第三方 cookie。它可能会自发中断或者注入恶意软件。这很无礼。)

Alan H 的解决方案 很棒,但是您不必使用 PHP 或任何其他服务器端编程语言。

至少如果您使用 Nginx。 :)

这是 Alan 解决方案的纯 nginx 服务器端配置:

server {


listen 80;
server_name third-party.example.com
  

# Don't allow user's browser to cache these replies
expires -1;
add_header Cache-Control "private";
etag off;
  

# The first third-party "JavaScript file" - served by nginx
location = /step1.js.php {
add_header Content-Type 'application/javascript; charset=UTF-8';
    

add_header Set-Cookie "third_party_c_t=hey there!;Max-Age=172800";
    

return 200 'window._3rd_party_test_step1_loaded();';
}
  

# The second third-party "JavaScript file" - served by nginx
location = /step2.js.php {
add_header Content-Type 'application/javascript; charset=UTF-8';
    

set $test 'false';
if ($cookie_third_party_c_t = 'hey there!') {
set $test 'true';
# clear the cookie
add_header Set-Cookie "third_party_c_t=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
    

return 200 'window._3rd_party_test_step2_loaded($test);';
}


}

附注:

  • 是的,是的,我知道 如果是恶魔,
  • 为了与 Alan 的“ HTML 测试页面”(third-party-cookies.html)完全兼容,我保留了以“ . php”结尾的名称,
  • 您还可以将两个位置的公共“设置 Content-Type 头”行移动到配置的 server部分(范围)-我保持这样,使其更像 Alan H 的解决方案。

这是一个纯 JS 解决方案,不需要任何服务器端代码,所以它可以从静态 CDN: https://github.com/mindmup/3rdpartycookiecheck工作——第一个脚本在代码中设置 cookie,然后重定向到第二个脚本,该脚本将向父窗口发送消息。

您可以使用 https://jsfiddle.net/tugawg8y/尝试一个现场版本。
注意,这个演示似乎不再工作。可能是 window.postMessage调用被阻塞。

客户端 HTML:

third party cookies are <span id="result"/>
<iframe src="https://mindmup.github.io/3rdpartycookiecheck/start.html"
style="display:none" />

客户端 JS:

 var receiveMessage = function (evt) {
if (evt.data === 'MM:3PCunsupported') {
document.getElementById('result').innerHTML = 'not supported';
} else if (evt.data === 'MM:3PCsupported') {
document.getElementById('result').innerHTML = 'supported';
}
};
window.addEventListener("message", receiveMessage, false);

当然,这要求客户机运行 JavaScript,这与基于服务器的解决方案相比是一个缺点; 另一方面,它更简单,而且您询问的是一个 JS 解决方案。

使用 URL 的白名单检测第三方 Cookie

Alan H & Gojko Adzic对于大多数用例来说已经足够好了,但是如果你想让你的用户只对某些域进行第三方 cookie 的白名单列表,这些解决方案就不起作用了。

我现在展示的是略微修改过的 Gojko Adzic回答版本

为此,我们需要两个域:

  • 域1,这是您的用户登录的页面,它最初设置 tpc=pending,然后重定向到域2
  • Domain 2在 iFrame 中注入 Domain 1的 URL,尝试设置 Cookie tpc=true并重定向回 Domain 1
  • 现在,域1读取 Cookie tpc并检查它的 true,如果我们得到的值为真,第三方 Cookie 是允许的,如果它仍然在 pending第三方 Cookie 被阻塞。

现在,你可以要求你的用户白名单(允许第三方 cookie) 仅此而已Domain 1,也就是通过这种技术第三方检测将 accurate如果用户白名单你的域名。


这是在 Chrome 74,75,76和 Edge 78中测试的

遗憾的是,Mozilla 并不像 Chrome 那样提供网址白名单,而 Safari 有自己的检测第三方 cookie (ITP)的机制。

另外,有时间的话,我会把这个演示上传到我的 github 上。

我的解决方案是从设置 cookie 的外部域加载 < script > ,检查是否成功,然后将结果(1或0)作为参数传递给回调函数。

HTML:

<script>
function myCallback(is_enabled) {
if (is_enabled===1) {//third party cookies are enabled
}
}
</script>
<script src="https://third-party-domain/third-party-cookies.php?callback=myCallback"></script>

如果您喜欢异步运行它,可以使用异步和延迟属性。

这也适用于 jQuery:

<script>
$.ajax({
url: 'https://third-party-domain/third-party-cookies.php',
dataType: 'jsonp',
}).done(function(is_enabled) {
if (is_enabled===1) {//third party cookies are enabled
}
})
</script>

下面是第三方的 cookie. PHP 代码,它必须驻留在不同的域中,服务器必须支持 PHP:

<?php


header('Cache-Control: no-store');
header('Content-Type: text/javascript');


if ($_GET['callback']=='') {
echo 'alert("Error: A callback function must be specified.")';
}
elseif (!isset($_GET['cookieName'])) {// Cookie not set yet
$cookieName = strtr((string)$_SERVER['UNIQUE_ID'], '@', '_');
while (isset($_COOKIE[$cookieName]) || $cookieName=='') {
$cookieName = dechex(mt_rand());// Get random cookie name
}
setcookie($cookieName, '3rd-party', 0, '/');
header('Location: '.$_SERVER['REQUEST_URI'].'&cookieName='.$cookieName);
}
elseif ($_COOKIE[$_GET['cookieName']]=='3rd-party') {// Third party cookies are enabled.
setcookie($_GET['cookieName'], '', -1, '/'); // delete cookie
echo $_GET['callback'].'(1)';
}
else {// Third party cookies are not enabled.
echo $_GET['callback'].'(0)';
}

检查第三方 cookie 是否启用了 格雷格Alan 的解决方案的步骤:

我修改文件,因为我唯一需要的是检查第三方 Cookie 是否启用,取决于我会做一些事情,如路由他们到一个页面告诉用户启用第三方 Cookie。

1)编辑你的 nginx 站点配置

(在 debian 9中,是在/etc/nginx/sites- 启用/默认)

$ sudo nano /etc/nginx/sites-enabled/default

您需要在您的域上有 TLS/SSL,否则您将无法设置 来自第三方的饼干域,您将得到一个错误说:

因为 Cookie 的 SameSite 属性未设置或无效,所以默认为 SameSite = Lax,这将防止 Cookie 在跨站点请求中被发送。这种行为可以保护用户数据不被意外泄露给第三方和跨站请求伪造。通过更新 Cookie 的属性来解决这个问题: 如果 Cookie 应该以跨站点请求的形式发送,则指定 SameSite = None 和 Secure。这允许第三方使用。如果 Cookie 不应该以跨站点请求的形式发送,则指定 SameSite = Strick 或 SameSite = Lax。

  • 在“访问控制-允许-起源”中指定允许的域,不建议使用“ *”(公共访问)。

  • 您可以指定“ Access-Control-allow-Ways“ GET”;”作为正在使用的唯一方法。

(我将这些标题设置为“ *”(public) ,只是为了确保它能正常工作,之后,您可以对它们进行编辑。)

您可以更改端点的名称(step 1.js.php & step 2.js.php) ,但是您需要在 js 脚本中更改它。(除非你更改 your-custom-domain.com/step1.js.php ,否则 your-custom-domain.com/step2.js.php 会被要求更改。扩展名并不重要,您可以将其更改为“ step 1”和“ step 2”或任何您喜欢的名称)

# Nginx config start
server {
server_name your-custom-domain.com;
# Check if third party cookies are allowed
# The first third-party "JavaScript file" - served by nginx
location = /step1.js.php {
expires -1;
add_header Cache-Control "private";
etag off;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "*";
add_header Content-Type 'application/javascript; charset=UTF-8';
add_header Set-Cookie "third_party_c_t=hey there!;Max-Age=172000; Secure; SameSite=none";
return 200 'window._3rd_party_test_step1_loaded();';
}
# The second third-party "JavaScript file" - served by nginx
location = /step2.js.php {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "*";
add_header Content-Type 'application/javascript; charset=UTF-8';
set $test 'false';
if ($cookie_third_party_c_t = 'hey there!') {
set $test 'true';
# clear the cookie
add_header Set-Cookie "third_party_c_t=;expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; SameSite=none";
}
return 200 'window._3rd_party_test_step2_loaded($test);';
}


# managed by Certbot, here is where your certificates goes.
listen [::]:443 ssl ipv6only=on;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/www.couchdb.me/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/www.couchdb.me/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

保存它(ctrl + x-如果你想保存它,请按字母 Y-回车确认)并重新启动/加载你的 nginx:

$ sudo systemctl restart nginx.service

2)登陆页面(不同于 your-custom-domain.com )

您可以更改方法的名称(_ 3rd _ party _ test _ step 1 _ load 和 _ 3rd _ party _ test _ step 2 _ load) ,但是您还需要在 nginx 的配置中更改它。(确保名称是唯一的)

2.1)将这个脚本添加到 html 的头部(这必须首先加载) :

<script type="text/javascript">
window._3rd_party_test_step1_loaded = function () {
// At this point, a third-party domain has now attempted to set a cookie (if all went to plan!)
var step2El = document.createElement("script");
const url = your-custom-domain.com + "/step2.js.php";
step2El.setAttribute("src", url);
document.head.appendChild(step2El);
};
window._3rd_party_test_step2_loaded = function (cookieSuccess) {
// If true, the third-party domain cookies are enabled
// If false, the third-party domain cookies are disable
cookieSuccess ? callMethodIfTrue() : callMethodIfFalse();
};
</script>

2.2)在你的 body 后面添加脚本 html:

<script type="text/javascript" src="https://your-custom-domain/step1.js.php"></script>

或者如果你在一个 js 文件中工作(记住,你需要把你的文件添加到你的 html 登陆页面,例如: <script type="text/javascript" src="path/to/your/js/file"></script>

JS 文件:

window._3rd_party_test_step1_loaded = function () {
// At this point, a third-party domain has now attempted to set a cookie (if all went to plan!)
var step2El = document.createElement("script");
const url = that.$url + "/step2.js.php";
step2El.setAttribute("src", url);
document.head.appendChild(step2El);
};
window._3rd_party_test_step2_loaded = function (cookieSuccess) {
// If true, the third-party domain cookies are enabled
// If false, the third-party domain cookies are disable
cookieSuccess ? callMethodIfTrue() : callMethodIfFalse();
};


window.onload = function () {
const url = "your-custom-domain.com" + "/step1.js.php";
var step1El = document.createElement("script");
step1El.setAttribute("src", url);
document.body.appendChild(step1El);
};

我这样做是为了检查第三方 cookie 是否被用户屏蔽。

我只是想进入浏览器的本地存储。如果用户启用了第三方 cookie,那么它应该是可用的,否则它将抛出一个错误。

try {
if(window.localStorage) {
//cookies enabled
}
} catch (err) {
//cookies disabled
}