CSRF攻击学习笔记

CSRF攻击简介

关键处理中引入的安全隐患:

Web应用中,用户登录后执行的操作中有些处理一旦完成就无法撤销。比如用户使用信用卡支付、从用户的银行账号转账、发送邮件、更改密码或邮箱地址等都是关键处理的典型案例。

关键处理中如果存在安全隐患,就会产生名为跨站请求伪造(Cross-Site Request Forgeries,简称CSRF)的漏洞。

CSRF介绍:

在执行关键处理前,需要确认该请求是否确实由用户自愿发起。如果忽略了这个步骤,就可能出现很大的问题,比如用户只是流量恶意网站,浏览器就擅自执行关键处理等。

引发上述问题的安全隐患为称为跨站请求伪造(CSRF)漏洞,而针对CSRF漏洞进行的攻击就是CSRF攻击。

Web应用存在CSRF漏洞时就可能会遭受如下攻击:

  • 使用用户的账号购物
  • 删除用户账号
  • 使用用户的账号发布帖子
  • 更改用户密码或邮箱地址等。

CSRF漏洞造成的影响仅限于应用的关键处理被恶意使用,而像用户的个人信息等就无法通过CSRF攻击窃取。

因此,为了预防CSRF漏洞,就需要在执行关键处理前确认请求确实是由用户自愿发起的。

CSRF漏洞总览:

产生地点: 以下任一网站上执行关键处理的页面。

  • 仅使用Cookie进行会话管理的网站。
  • 仅依靠HTTP认证、SSL客户端证书、手机的移动ID来识别用户的网站。

影响范围:

  • 存在CSRF漏洞的页面。

影响类型:

  • 以受害用户的权限执行关键处理。如购买商品、发布帖子、更改密码等。

影响程度:

  • 中~大

用户参与程度:

  • 需要–>点击恶意链接、浏览恶意网站等。

对策概要:

  • 执行关键处理前,确认是正规用户发起的请求。

两种典型的CSRF攻击模式:

CSRF漏洞实施的两种典型的攻击模式。

  1. 输入–执行 这种简单模式的攻击。
  2. 中途包含确认页面时的攻击。

输入–执行攻击模式:

  1. 用户登录网站。
  2. 攻击者设下圈套。
  3. 受害人浏览恶意网站而出发圈套。
  4. 攻击者使用恶意网站中的JavaScript,使受害人的浏览器向攻击对象网站发送将密码为攻击者指定的密码的POST请求。
  5. 密码被更改。

存在确认页面的CSRF攻击:

  • Hidden参数
  • 使用会话变量

根据同源策略,从iframe的外层(恶意网页)无法读取到内层(攻击对象)的内容,因此,CSRF攻击虽然能够以正规用户的权限恶意使用攻击对象网站中的关键处理,却无法获取网页中显示的内容。

但是,在使用CSRF攻击成功更改用户密码后,攻击者就知道了更改后的密码,从而也就能够登陆应用来窃取被害人的信息了。

修改密码的三个条件

  1. 使用POST方法请求
  2. 保持登录状态。
  3. 使用POST参数中的pwd指定新密码

CSRF攻击与XSS攻击对比:

  • XSS:利用用户对站点的信任
  • CSRF:利用站点对已经身份认证用户的信任

CSRF是指恶意使用用户对服务器正常请求中的请求处理,恶意使用的内容仅限于服务器端提供的操作。

而XSS,在用户请求服务器中包含的脚本被原封不动的以响应的形式返回,随后该恶意脚本在用户的浏览器中被执行。由于攻击者能够在用户的浏览器上执行自己准备的HTML或JavaScript,因此只要是浏览器能做到的事情都可以被用作攻击手段。攻击者甚至还能够通过JavaScript恶意使用服务器端的功能。

CSRF攻击安全解决方案

对策分析:

防御CSRF的关键为确认关键处理的请求确实是由正规用户自愿发送的。因此,作为CSRF的防范策略,需要执行以下两点:

  1. 筛选出需要防范CSRF攻击的页面
  2. 使代码有能力辨认是否是正规用户的自愿请求。

筛选出需要防范CSRF攻击的页面:

并非所有的页面都需要实施CSRF防御策略,事实上无需防范CSRF的页面居多。通常情况下,Web应用的入口并非只有一处,通过搜索引擎、社交书签、其他链接等方式都能进入Web应用中的各种页面。比如EC(电子商务)网站一般就非常欢迎通过外部链接进入到它的商品展示页面。而这像这种页面就不需要实施CSRF对策。

而另一方面,EC网站中的购买商品、更改密码或确认个人信息等页面,就不能够任意由其他网站随意执行。这样的页面就应当实施CSRF防范策略。

确认是正规用户自愿发送的请求:

判断请求是否为正规用户自愿发送的实现方法,一般有如下3类:

  • 嵌入机密信息(令牌)

    如果访问需要防范CSRF的页面(登录页面、订单确认页面等)时需要提供第三方无法得知的机密信息的话,那么即使出现非正规用户自愿发送的请求,应用端也能够通过判断得知请求是否合法。用于此目的的机密信息被称为令牌(Token)。会话ID就是一种既简单又安全的令牌实现方法。

    接收令牌的请求(接受关键处理的请求)必须为POST方法。因为使用GET方法发送机密信息的话,令牌信息就可能通过Referer泄露出去。

  • 再次输入密码

    让用户在此输入密码,也是用来确认请求是否是由用户自愿发起的一种方法。除了用来防范CSRF攻击,在此输入密码也可以被用于其他目的。

    • 在用户确认下订单之前,再次向用户确认购买意向。
    • 能够确认此事在电脑前操作的确实是用户本人。

    要求确认密码的页面都应该是在最后的执行页面。如果仅在途中的某个页面就行密码确认,根据代码实现方法还是可能会存在CSRF漏洞,所以要求输入密码的实际非常重要。

  • 检验Referer

    在执行关键处理的页面确认Referer,也是CSRF的一种防范策略。正规请求中Referer的值应该为执行页面的上一个页面(输入页面或者确认页面等)的URL,这一点一定要得到确认。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
    session_start();
    if(preg_match('#\Ahttp://www.baidu.com', @$_SERVER['HTTP_REFERER']) != 1){
    die('REFERER检查失败');
    }
    if(session_id() !== $_POST['token']){
    die('请从正规页面进行操作');
    }
    ?>

CSRF防范策略比较:

嵌入令牌 再次输入密码 确认Referer
开发耗时
对用户的影响 增加了输入密码的麻烦 关闭了Referer的用户无法正常使用
能否用于手机网站 不可
建议使用的地方 最基本的防御策略,所有情况下均可使用 需要防范他人伪装或者确认需要很强的页面 用户能够限定用户环境的既有应用的CSRF防范策略。

CSRF的辅助性对策:

执行完关键处理后,建议向用户注册的邮箱发送有关键处理内容的通知邮件。

发送通知邮件虽然不能防范CSRF攻击,但是在万一遭受了CSRF攻击的情况下能在第一时间让用户知情,从而将损害降到最低。

另外,除了CSRF攻击之外,在攻击者通过XSS攻击伪装成用户操作关键处理时,发送通知邮件也能使用户尽早发现。

但是,由于邮件是未经加密的明文传输,因此,最好不要在邮件中添加重要信息,而只是通知用户有人恶意执行了关键处理如果用户想要了解详情的话,可以登录Web应用查看购买历史或者发送历史等内容。

CSRF对策总结:

CSRF漏洞的根本防范策略如下:

  1. 筛选需要防范CSRF的页面
  2. 确认是正规用户自愿发起的请求。

其中,确认是正规用户自愿发起的请求的方法有以下三种。

  1. 嵌入机密信息(令牌)
  2. 再次输入密码
  3. 检验Referer

另外,作为CSRF漏洞的辅助性对策,可以执行以下操作。

  1. 执行完关键处理后,向用户注册的邮箱发送通知邮件。

自动化扫描程序的检测方法:

  • 在请求和响应过程中检查是否存在Anti_CSRF token
  • 检查服务器是否验证Anti-CSRF token
  • 检查token字符串是否可编辑和伪装
  • 检查Referer头字段是否可以伪装

DVWA的CSRF漏洞

安全级别是Low的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

if( 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!
// Update the database
// Feedback for the user
}
else {
// Issue with passwords matching
}
?>

原理:在安全级别为low的情况下,CSRF更改密码时,可以直接进行提交密码进行改密。没有安全控制。

攻击:可以通过在恶意站点上做超链接,引诱用户点击,从而直接更改密码。

安全级别是Medium的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( 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!
// Update the database
// Feedback for the user
}
else {
// Issue with passwords matching
}
}
else {
// Didn't come from a trusted source
}
?>

原理:在安全级别为Medium的情况下,主要通过stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false 验证了Referer字段,从检查是否来自本网站的更改密码的请求。

攻击:可以将恶意站点上恶意链接中的html页面名字改为包含合法站点域名字符串的文件名,从而让通过Referer字段的检查。

安全级别为High的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];

// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
// Update the database
// Feedback for the user
}
else {
// Issue with passwords matching
}
// Generate Anti-CSRF token
generateSessionToken();

?>

原理:通过服务器给客户端生成一个token,在更改密码时,还需要提交token字段,token可以可以使服务器发给客户端的一个随机数。那样恶意站点就不能知道token从而无法实现绕过用户直接更改密码。

攻击:但是如果合法网站上存在XSS漏洞,那么可以首先利用XSS漏洞,获取用户的Token,然后携带用户的Token去更改密码。从而达到攻击的效果。

安全级别为Impossible的情况:

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
<?php

if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$pass_curr = $_GET[ 'password_current' ];
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];

// Sanitise current password input
// Check that the current password is correct
// Do both new passwords match and does the current password match the user?
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// It does!
// Update database with new password
// Feedback for the user
}
else {
// Issue with passwords matching
}
}

// Generate Anti-CSRF token
generateSessionToken();
?>

原理:在这情况下,除了验证token,还需要用户输入旧密码,那么第三方恶意站点(非法用户)肯定无法知道旧密码,再加上token,基本杜绝了CSRF漏洞的攻击。


学习过程中,笔记的整理与资料的记录。


坚持原创技术分享,您的支持将鼓励我继续创作!