我有一个应用程序需要检查客户端浏览器是否启用了第三方 cookie。有谁知道如何在 JavaScript 中做到这一点?
检查是否启用了第三方 cookie
技术背景
第三方通过 HTTP(而不是 JavaScript)设置和读取 cookie。
所以我们需要向外部域发出两个请求来测试是否启用了第三方 cookie:
- 第三方设置 cookie 的地方
- 第二个,根据浏览器是否在第二个请求中将 cookie 发送回同一个第三方,具有不同的响应。
由于 DOM 安全模型,我们不能使用 XMLHTTPRequest (Ajax)。
显然您不能并行加载两个脚本,或者第二个请求可能在第一个请求的响应返回之前发出,并且测试 cookie 将不会被设置。
代码示例
鉴于:
该
.html
文件位于一个域中,并且这些
.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…';
// 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…</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'); ?>);
最后一行使用三元运算符输出文字 Javascripttrue
或false
取决于测试 cookie 是否存在。
在这里测试一下。
可在https://alanhogan.github.io/web-experiments/3rd/third-party-cookies.html为您提供测试乐趣。
(最后一点 -未经他人许可,请勿使用其他人的服务器来测试第三方 cookie。它可能会自发中断或注入恶意软件。这是不礼貌的。)
这是一个不需要任何服务器端代码的纯 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 解决方案。
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 内,但您无法查看饼干在那里。