PHP开发中csrf攻击的简单演示和防范


Posted in PHP onMay 07, 2017

csrf攻击,即cross site request forgery跨站(域名)请求伪造,这里的forgery就是伪造的意思。网上有很多关于csrf的介绍,比如一位前辈的文章CSRF的攻击方式详解,参考这篇文章简单解释下:csrf 攻击能够实现依赖于这样一个简单的事实:我们在用浏览器浏览网页时通常会打开好几个浏览器标签(或窗口),假如我们登录了一个站点A,站点A如果是通过cookie来跟踪用户的会话,那么在用户登录了站点A之后,站点A就会在用户的客户端设置cookie,假如站点A有一个页面siteA-page.php(url资源)被站点B知道了url地址,而这个页面的地址以某种方式被嵌入到了B站点的一个页面siteB-page.php中,如果这时用户在保持A站点会话的同时打开了B站点的siteB-page.php,那么只要siteB-page.php页面可以触发这个url地址(请求A站点的url资源)就实现了csrf攻击。

上面的解释很拗口,下面举个简单的例子来演示下。

1,背景和正常的请求流程

A站点域名为html5.yang.com,它有一个/get-update.php?uid=uid&username=username地址,可以看到这个地址可以通过get方法来传递一些参数,假如这个页面的逻辑是:它通过判断uid是否合法来更新username,这个页面脚本如下:

<?php
// 这里简便起见, 从data.json中取出数据代替请求数据库
$str = file_get_contents('data.json');
$data = json_decode($str, true);

// 检查cookie和请求更改的uid, 实际应检查数据库中的用户是否存在
empty($_COOKIE['uid']) ||empty($_GET['uid']) || $_GET['uid'] != $data['id'] ? die('非法用户') : '';
// 检查username参数
$data['username'] = empty($_GET['username']) ? die('用户名不能为空') : $_GET['username'];

// 更新数据
$data['username'] = $_GET['username'];
if(file_put_contents('data.json', json_encode($data))) {
  echo "用户名已更改为{$data['username']}<br>";
} else {
  die('更新失败');
}

正常情况下这个页面的链接是放在站点A下面的,比如A站点的csrfdemo.php页面,用户登录站点A以后可以通过点击这个链接来发送请求,比如站点A有一个页面脚本,包含了这个链接:

<?php
// 这里用一个data.json文件保存用户数据,模拟数据库中的数据
// 先初始化data.json中的数据为{"id":101,"username":"jack"}, 注意这句只让它执行一次, 然后把它注释掉
// file_put_contents('data.json','{"id":101,"username":"jack"}');

$data = json_decode(file_get_contents('data.json'), true);

// 这里为了简便, 省略了用户身份验证的过程
if ($data['username']) {
  // 设置cookie
  setcookie('uid', $data['id'], 0);
  echo "登录成功, {$data['username']}<br>";
}
?>

 <a href="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=json" rel="external nofollow" >
  更新用户名为json
 </a>

加载这个页面如下:

PHP开发中csrf攻击的简单演示和防范

用点击页面中的链接来到get-update.php页面:

PHP开发中csrf攻击的简单演示和防范

上面是正常的请求流程,下面来看B站点是如何实现csrf攻击的。

2,csrf攻击的最简单实现

B站点域名为test.yang.com,它有一个页面csrf.php,只要用户在维持A站点会话的同时打开了这个页面,那么B站点就可以实现csrf攻击。至于为什么会打开......,其实这种情景在我们浏览网页时是很常见的,比如我在写这篇博客时,写着写着感觉对csrf某个地方不懂,然后就百度了,结果百度出来好多结果,假如说有个网站叫csrf百科知识,这个网站对csrf介绍的非常详细、非常权威,那么我很可能会点进去看,但是这个网站其实是个钓鱼网站,它在某个访问频率很高的页面中嵌入了我博客编辑页面的url地址,那么它就可以实现对我博客的csrf攻击。好了,言归正传,下面来看下csrf.php脚本代码:

<?php
?>
<img src="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp">
可以看到上面的代码没有php代码,只有一个img标签,img标签的src就是A站点的那个更新用户名的链接,只不过把username改为了jsonp,访问站点B的csrf.php这个页面:

PHP开发中csrf攻击的简单演示和防范

下面再来访问下A站点的csrfdemo.php页面:

PHP开发中csrf攻击的简单演示和防范

可以看到用户名被修改为了jsonp。

简单分析下:B站点的这个csrf.php利用了html中的img标签,我们都知道img标签有个src属性,属性值指向需要加载的图片地址,当页面载入时,加载图片就相当于向src指向的地址发起http请求,只要把图片的地址修改为某个脚本地址,这样自然就实现了最简单的csrf攻击。如此说来,其实csrf很容易实现,只不过大家都是“正人君子”,谁没事会闲着去做这种“下三滥”的事情。但是害人之心不可有,防人之心不可无。下面看下如何简单防范这种最简单的csrf攻击。

3,简单防范措施

其实防范措施也比较简单,A站点可以在get-update.php脚本中判断请求头的来源,如果来源不是A站点就可以截断请求,下面在get-update.php增加些代码: 

<?php
// 检查上一页面是否为当前站点下的页面
if (!empty($_SERVER['HTTP_REFERER'])) {
  if (parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) != 'html5.yang.com') {
    // 可以设置http错误码或者指向一个无害的url地址
    //header('HTTP/1.1 404 not found');
    //header('HTTP/1.1 403 forbiden');
    header('Location: http://html5.yang.com/favicon.ico');
    // 这里需要注意一定要exit(), 否则脚本会接着执行
    exit();
  }
 }

$str = file_get_contents('data.json');
// 代码省略

但是,这样就万事大吉了吗,如果http请求头被伪造了呢?A站点升级了防御,B站点同时也可以升级攻击,通过curl请求来实现csrf,修改B站点的csrf.php代码如下:

<?php
$url = 'http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp';
$refer = 'http://html5.yang.com/';
// curl方法发起csrf攻击
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
// 设置Referer
curl_setopt($ch, CURLOPT_REFERER, $refer);
// 这里需要携带上cookie, 因为A站点get-update.php对cooke进行了判断
curl_setopt($ch, CURLOPT_COOKIE, 'uid=101');
curl_exec($ch);
curl_close($ch);
?>
<img src="http://html5.yang.com/csrfdemo/get-update.php?uid=101&username=jsonp">
这样同样可以实现csrf攻击的目的。那么就没有比较好的防范方法了吗?

4,小结

下面我们回到问题的开始,站点A通过cookie来跟踪用户会话,在cookie中存放了重要的用户信息uid,get-update.php脚本通过判断用户的cookie正确与否来决定是否更改用户信息,看来靠cookie来跟踪会话并控制业务逻辑是不太安全的,还有最严重的一点:get-update.php通过get请求来修改用户信息,这个是大忌。所以站点A可以接着升级防御:用session来代替cookie来跟踪用户会话信息,将修改用户信息的逻辑重写,只允许用post方法来请求用户信息。站点B同样可以升级攻击:curl可以构造post请求,劫持session等等,不过这些我还没研究过,后续再说吧。

PHP 相关文章推荐
使用sockets:从新闻组中获取文章(三)
Oct 09 PHP
PHP+javascript液晶时钟
Oct 09 PHP
什么是MVC,好东西啊
May 03 PHP
php对gzip文件或者字符串解压实例参考
Jul 25 PHP
Windows Apache2.2.11及Php5.2.9-1的安装与配置方法
Jun 08 PHP
采集邮箱的php代码(抓取网页中的邮箱地址)
Jul 17 PHP
php中3种方法删除字符串中间的空格
Mar 10 PHP
php生成QRcode实例
Sep 22 PHP
php抽奖概率算法(刮刮卡,大转盘)
Apr 17 PHP
java微信开发之上传下载多媒体文件
Jun 24 PHP
PHP实现求解最长公共子串问题的方法
Nov 17 PHP
tp5.1 框架数据库高级查询技巧实例总结
May 25 PHP
ThinkPHP框架实现数据增删改
May 07 #PHP
thinkphp 验证码 的使用小结
May 07 #PHP
解析 thinkphp 框架中的部分方法
May 07 #PHP
ThinkPHP 模板引擎使用详解
May 07 #PHP
php中Ioc(控制反转)和Di(依赖注入)
May 07 #PHP
Laravel中任务调度console使用方法小结
May 07 #PHP
Laravel实现表单提交
May 07 #PHP
You might like
不错的一篇面向对象的PHP开发模式(简写版)
2007/03/15 PHP
php中取得文件的后缀名?
2012/02/20 PHP
PHP函数in_array()使用详解
2014/08/20 PHP
访问编码后的中文URL返回404错误的解决方法
2014/08/20 PHP
php生成gif动画的方法
2015/11/05 PHP
JAVASCRIPT keycode总结
2009/02/04 Javascript
jquery控制listbox中项的移动并排序的实现代码
2010/09/28 Javascript
JavaScript OOP面向对象介绍
2010/12/02 Javascript
javascript时区函数介绍
2012/09/14 Javascript
Extjs 继承Ext.data.Store不起作用原因分析及解决
2013/04/15 Javascript
Ajax同步与异步传输的示例代码
2013/11/21 Javascript
用javascript读取xml文件读取节点数据
2014/08/12 Javascript
jQuery老黄历完整实现方法
2015/01/16 Javascript
深入理解JavaScript系列(43):设计模式之状态模式详解
2015/03/04 Javascript
JS 对象属性相关(检查属性、枚举属性等)
2015/04/05 Javascript
Jquery日期选择datepicker插件用法实例分析
2015/06/08 Javascript
浅谈JavaScript超时调用和间歇调用
2015/08/30 Javascript
JS简单循环遍历json数组的方法
2016/04/22 Javascript
详解angularJs中自定义directive的数据交互
2017/01/13 Javascript
angular实现商品筛选功能
2017/02/01 Javascript
详解如何使用babel进行es6文件的编译
2018/05/29 Javascript
利用JS动态生成隔行换色HTML表格的两种方法
2018/10/09 Javascript
[01:11:21]DOTA2-DPC中国联赛 正赛 VG vs Elephant BO3 第一场 3月6日
2021/03/11 DOTA
python局域网ip扫描示例分享
2014/04/03 Python
Python多进程multiprocessing.Pool类详解
2018/04/27 Python
利用Python如何实现数据驱动的接口自动化测试
2018/05/11 Python
Python多进程原理与用法分析
2018/08/21 Python
python实现ip代理池功能示例
2019/07/05 Python
澳大利亚自然和有机的健康美容产品一站式商店:Ziani Beauty
2017/12/28 全球购物
俄罗斯在线服装店:STOLNIK
2021/03/07 全球购物
机电一体化应届生求职信范文
2014/01/24 职场文书
2015年三万活动总结
2015/03/25 职场文书
检讨书模板大全
2015/05/07 职场文书
详解Nginx启动失败的几种错误处理
2021/04/01 Servers
Java中使用Filter过滤器的方法
2021/06/28 Java/Android
基于Python编写简易版的天天跑酷游戏的示例代码
2022/03/23 Python