指的一提的是,一般情况下如果设置SameSite为None,也要同时设置Secure,否则一些浏览器可能会拒绝仅有SameSite=None的设置。
在不同浏览器上的实验
在test1.com上部署一个设置cooke的前端页面test1.com/cookieSetting.html和一个后端处理脚本test1.com/getCookie.php,还有一个测试cookie是否携带的页面test1.com/json.php,如果携带cookie就会返回账户密码,否则返回error。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Cookie 设置与清除</title> </head> <body> <h2>Cookie 设置与清除表单</h2> <form action="./getCookie.php" method="post" id="cookieForm"> <label for="samesite">SameSite:</label> <select id="samesite" name="samesite"> <option value="Strict">Strict</option> <option value="Lax">Lax</option> <option value="None">None</option> <option value=" ">不设置</option> </select> </br> <label for="httponly">HttpOnly:</label> <input type="checkbox" id="httponly" name="httponly"> <label for="secure">Secure:</label> <input type="checkbox" id="secure" name="secure"> </br> <select id="setcookie" name="setcookie"> <option value="set">设置cookie</option> <option value="">清除cookie</option> </select> </br> <button type="submit">确认</button> </form> </body> </html>test1.com/getCookie.php
<?php $simesite=$_POST["samesite"]; $httponly=$_POST["httponly"]; $secure=$_POST["secure"]; $set=$_POST["setcookie"]; // 堆代码 duidaima.com // 检查是否存在名为 "secret" 的Cookie if ($set==="") { // 如果存在,则清除Cookie setcookie('secret', '', time() - 3600); // 将过期时间设置为过去的时间 echo 'Cookie已清除'; } else { // 如果不存在,则设置Cookie为 "secret=666666" // 启用HTTPS时设置Secure标志 $secureFlag = true; // 设置Cookie setcookie('secret', '666666', [ 'expires' => time() + 3600, // 0表示会话结束时过期 'path' => '/', // 可在整个域名下访问 'domain' => '', // 通过任何子域名都可以访问 'secure' => $secure, // 根据是否使用HTTPS动态设置Secure标志 'httponly' => $httponly, // 仅通过HTTP协议访问,防止JavaScript访问 'samesite' => $simesite, // 允许在跨站请求中发送Cookie ]); echo 'Cookie已设置'; } ?> test1.com/json.php <?php if(isset($_COOKIE['secret']) && $_COOKIE['secret'] == '666666') { header("Content-Type: text/json"); echo "hack({\"username\":\"admin\",\"password\":\"123456\"})"; } else { echo 'error'; } ?>在test2.com则部署一个跨域测试的html页面crossSite.html,来测试各种跨域的cookie携带情况
<a href="https://test1.com/json.php?a">link to test1.com/json.com</a> </br> <button onclick="window.open('https://test1.com/json.php?windowOpen')">window.open("https://test1.com/json.php")</button> </br> <button onclick="window.location.href = 'https://test1.com/json.php?windowLocation'">window.location.href = 'https://test1.com/json.php'</button> <form action="https://test1.com/json.php" method="get"> <input name="get" type="text hidden" value="get"/> <button type="submit">form get</button> </form> <form action="https://test1.com/json.php" method="post"> <input name="post" type="text hidden" value="post"/> <button type="submit">form post</button> </form> <iframe src="https://test1.com/json.php?iframe"></iframe> <script src="https://test1.com/json.php?script"></script> <link rel="stylesheet" href="https://test1.com/json.php?stylesheet"> <script> fetch('https://test1.com/json.php?fetch', { method: 'GET', // 或 'POST',根据实际需求选择 credentials: 'include' // 允许携带 Cookie }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); </script> <script> var xhr = new XMLHttpRequest(); xhr.open('GET', 'https://test1.com/json.php?ajax', true); xhr.withCredentials = true; // 允许携带 Cookie xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { var data = JSON.parse(xhr.responseText); console.log(data); } else { console.error('Error:', xhr.status); } } }; xhr.send(); </script> <link rel="prefetch" href="https://test1.com/json.php?prefetch">测试步骤很简单,先通过test1.com/cookieSetting.html设置不同属性的cookie,然后通过test2.com/crossSite.html进行跨域访问,检查其cookie携带情况即可,这里大量图片就省略了,我直接给出我的测试结果。
浏览器 | samesite=strick | samesite=lax | samesite=none secure=false | samesite=none secure=true | 不设置samesite也不设置secure |
---|---|---|---|---|---|
firefox浏览器 Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0 | 全部不携带cookie | 与标准有一处不同,<form method=post>携带cookie | 设置成功,全部携带cookie | 与samesite=none secure=false相同 | 此时secure=false,而samesite=none。全部携带cookie。 |
edge浏览器 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0 | 全部不携带cookie | 与之前的总结完全相同,在对<form method=post>的处理上,与firefox不同 | edge拒绝这样设置cookie | 全部携带cookie | 此时secure和samesite都没有显示值。经过测试,本文第一张图中的跨站定级跳转全部携带cookie,其他不携带,post值得注意 |
chrome浏览器 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 | 与edge一致 | 与edge一致 | 与edge一致 | 与edge一致 | 与edge一致 |
关于CSRF、JSONP和CORS的cookie跨域
经过上面的分析,在什么时候可以进行跨域其实已经很明了了:
1.CSRF使用get和input进行跨域,当使用get进行跨域时,除了SameSite=strick的情况,都可以携带cookie;当使用post进行跨域时,按照标准,只有SameSite=None和Secure被设置时才能携带cookie。但是由于很多浏览器的实现不标准或者对于不设置SameSite的处理较为模糊,有时不设置SameSite或设置SameSite=None而不设置Secure时允许input跨域携带cookie,这要视浏览器的实现而定。