freeBuf
CRSF、JSONP劫持、CORS配置不当中的cookie跨域问题
2024-03-15 20:09:18

前言

最近在挖SRC时遇到了JOSNP和CORS,但是进行JSONP劫持和CORS跨域请求的尝试却失败了,后来经过研究发现根本原因是cookie跨域的限制造成了,遂有了这篇文章。

关于cookie跨域的分析

影响cookie跨域的samesite和secure

2016年开始,Chrome 51版本对Cookie新增了一个 SameSite属性,为了防止CSRF攻击,陆续的各大厂商的浏览器也都适配了该属性,该属性限制了在不同情况下cookie的跨域。而Secure则是规定了cookie是否可以在http下传输,如果Secure为true,则仅在使用https时才会携带cookie。

  • 下图为不同SameSite设置的效果,当然也可以不设置SameSite,大多数浏览器在不设置SameSite时默认为Lax

img

指的一提的是,一般情况下如果设置SameSite为None,也要同时设置Secure,否则一些浏览器可能会拒绝仅有SameSite=None的设置。

img

在不同浏览器上的实验

上面的描述只是标准,具体的效果还要依赖于浏览器的实现,这里进行一波实验来验证一下上述和标准和各个浏览器的实现。

先使用小皮面板创建两个站点,test1.com和test2.com,test1.com注意设置为https访问。

img

img

在test1.com上部署一个设置cooke的前端页面test1.com/cookieSetting.html和一个后端处理脚本test1.com/getCookie.php,还有一个测试cookie是否携带的页面test1.com/json.php,如果携带cookie就会返回账户密码,否则返回error。

  • test1.com/cookieSetting.html

<!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"];

// 检查是否存在名为 "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携带情况

  • test2.com/crossSite.html

<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=stricksamesite=laxsamesite=none secure=falsesamesite=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跨域

经过上面的分析,在什么时候可以进行跨域其实已经很明了了

  • CSRF使用get和input进行跨域,当使用get进行跨域时,除了SameSite=strick的情况,都可以携带cookie;当使用post进行跨域时,按照标准,只有SameSite=None和Secure被设置时才能携带cookie。但是由于很多浏览器的实现不标准或者对于不设置SameSite的处理较为模糊,有时不设置SameSite或设置SameSite=None而不设置Secure时允许input跨域携带cookie,这要视浏览器的实现而定。

  • JSONP劫持依赖<script src>访问敏感信息,如果希望<script src>携带cookie,只有SameSite=None的时候才可以

  • CORS配置不当,对于CORS的设置就不多说了,在Access-Control-Allow-Credentials为true的前提下,由于CORS的跨域依赖的是ajax和fetch,要在这种情况下携带cookie进行跨域,同样也只有SameSite=None的时候才可以

本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
文章目录