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 相关文章推荐
用PHP实现多服务器共享SESSION数据的方法
Mar 16 PHP
关于PHP的相似度计算函数:levenshtein的使用介绍
Apr 15 PHP
解析thinkphp的左右值无限分类
Jun 20 PHP
javascript+php实现根据用户时区显示当地时间的方法
Mar 11 PHP
Thinkphp模板标签if和eq的区别和比较实例分析
Jul 01 PHP
PHP实现根据时间戳获取周几的方法
Feb 26 PHP
php图片添加文字水印实现代码
Mar 15 PHP
mac下多个php版本快速切换的方法
Oct 09 PHP
利用php操作memcache缓存的基础方法示例
Aug 02 PHP
PHP基于PDO调用sqlserver存储过程通用方法【基于Yii框架】
Oct 07 PHP
php微信开发之音乐回复功能
Jun 14 PHP
PHP使用SOAP调用API操作示例
Dec 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/02/14 PHP
php下过滤HTML代码的函数
2007/12/10 PHP
ThinkPHP模板比较标签用法详解
2014/06/30 PHP
函数中使用require_once问题深入探讨 优雅的配置文件定义方法推荐
2014/07/02 PHP
php获取客户端电脑屏幕参数的方法
2015/01/09 PHP
PHP接口并发测试的方法(推荐)
2016/12/15 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
PHP学习记录之常用的魔术常量详解
2019/12/12 PHP
javascript 操作符(~、&amp;、|、^、)使用案例
2014/12/31 Javascript
JS实现控制表格内指定单元格内容对齐的方法
2015/03/30 Javascript
JavaScript面向对象的实现方法小结
2015/04/14 Javascript
JavaScript中的Math.sin()方法使用详解
2015/06/15 Javascript
AngularJS HTML DOM详解及示例代码
2016/08/17 Javascript
JavaScript中绑定事件的三种方式及去除绑定
2016/11/05 Javascript
Angular ng-repeat遍历渲染完页面后执行其他操作详细介绍
2016/12/13 Javascript
微信小程序实现页面跳转传值的方法
2017/10/12 Javascript
判断JavaScript中的两个变量是否相等的操作符
2019/12/21 Javascript
[01:38]DOTA2 2015国际邀请赛中国区预选赛 Showopen
2015/06/01 DOTA
[00:52]DOTA2齐天大圣预告片
2016/08/13 DOTA
python实时分析日志的一个小脚本分享
2017/05/07 Python
python实现数据预处理之填充缺失值的示例
2017/12/22 Python
Jupyter notebook在mac:linux上的配置和远程访问的方法
2019/01/14 Python
解决yum对python依赖版本问题
2019/07/05 Python
python模拟键盘输入 切换键盘布局过程解析
2019/08/15 Python
基于python及pytorch中乘法的使用详解
2019/12/27 Python
python实现全排列代码(回溯、深度优先搜索)
2020/02/26 Python
python在一个范围内取随机数的简单实例
2020/08/16 Python
解决Python 函数声明先后顺序出现的问题
2020/09/02 Python
英国书籍、CD、DVD和游戏的第一道德零售商:Awesome Books
2020/02/22 全球购物
Can a struct inherit from another class? (结构体能继承类吗)
2014/07/22 面试题
汽车驾驶求职信
2013/10/25 职场文书
生产厂长岗位职责
2014/02/21 职场文书
保险内勤岗位职责
2014/04/05 职场文书
党的群众路线教育实践活动组织生活会发言材料
2014/10/17 职场文书
JavaScript中MutationObServer监听DOM元素详情
2021/11/27 Javascript
JavaWeb Servlet开发注册页面实例
2022/04/11 Java/Android