检查是否启用了第三方 cookie

IT技术 javascript cookies
2021-01-29 21:34:17

我有一个应用程序需要检查客户端浏览器是否启用了第三方 cookie。有谁知道如何在 JavaScript 中做到这一点?

6个回答

技术背景

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

所以我们需要向外部域发出两个请求来测试是否启用了第三方 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'); ?>);

最后一行使用三元运算符输出文字 Javascripttruefalse取决于测试 cookie 是否存在。

在这里测试一下

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

(最后一点 -未经他人许可,请勿使用其他人的服务器来测试第三方 cookie。它可能会自发中断或注入恶意软件。这是不礼貌的。)

我从上面删除了一些过时的评论,但我在@MaheshKulkarni 的有用说明的几天内修复了我的示例测试。由于我以前的网络主机在处理我的 SSL 续订时遇到了史诗般的失败,我会遇到问题。如果以后测试中断,请告诉我:alanhogan.com/contact?reason=3rd%20party%20cookie%20test
2021-03-29 21:34:17
这仍然有效吗?我在 chrome 和 safari 上检查了你的链接(两次都是在 mac 上),但消息仍然存在。检查员显示 chrome 阻止对 img 的请求,因为它不安全(并且主域是 https)?
2021-03-30 21:34:17
这似乎不适用于 iOS Safari。有任何想法吗?
2021-04-03 21:34:17
@biko 您可能会遇到“智能跟踪保护”,请参阅webkit.org/blog/7675/intelligent-tracking-prevention
2021-04-09 21:34:17
@AlanH,您知道此解决方案目前是否仍在 Chrome(一般基于 Chromium 的浏览器)中有效?它在 Firefox 中适用于我,但不适用于 Chrome 或 Brave。即使在设置中允许所有 cookie,第三方 cookie 也始终显示为已禁用。如果有这方面知识的人可以插话,我将不胜感激。
2021-04-12 21:34:17

这是一个不需要任何服务器端代码的纯 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 解决方案。

如果start.html文件应该消失,文件的内容是:<body> <script> if (window.parent) { if (/thirdparty=yes/.test(document.cookie)) { window.parent.postMessage('MM:3PCsupported', '*'); } else { window.parent.postMessage('MM:3PCunsupported', '*'); } } </script> </body>
2021-03-18 21:34:17
@TheStoryCoder 发布的内容属于该文件complete.html文件的内容start.html如下所示:<script> document.cookie="thirdparty=yes"; document.location="complete.html"; </script> 这会设置一个 cookie 并触发对complete.html文件的重定向
2021-03-24 21:34:17
这是迄今为止最好的答案
2021-03-27 21:34:17
我确认此设置有效。它没有从该Fidlle链接在Chrome中的我,但它建立后在该处它的工作。我唯一需要改变的是删除window.onload处理程序,这显然是 Fiddle 放置用户代码的地方。
2021-04-02 21:34:17
似乎被最新的 Chrome 更新打破了,其中相同站点的限制非常严格
2021-04-04 21:34:17

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);';
  }

}

边注:

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

我的解决方案通过从设置 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>

如果您更喜欢异步运行它,您可以使用 async 和 defer 属性。

这也适用于 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>

这是第三方cookies.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,然后检查该 cookie 是否存在。但是,标准浏览器安全性不允许域 A 中的脚本对域 B、C 等上设置的 cookie 执行任何操作……例如,您无法访问“外部”cookie。

如果您有一些特定的用途,例如检查广告是否被阻止(这也会阻止 3rd 方跟踪 cookie),您可以检查广告服务器的内容是否在页面的 DOM 内,但您无法查看饼干在那里。

确实,我已经尝试过了,但它不起作用。我们实际上是在白标域上运行一个白标应用程序,但我们想放置我们自己的 cookie。对 cookie 的默认检查是不够的,因为我们的 cookie 被视为第三方。我们现在将尝试不同的方法。
2021-04-12 21:34:17