PHP中的session安全吗?


Posted in PHP onJanuary 22, 2016

做PHP开发这么长时间,还真没有真正关注过安全的问题,每次都是以完成项目为主,最近在网上看到了一篇关于安全的文章,看完以后才注意到自己以前的项目都存在着很大的安全漏洞,于是挑了一个项目进行了测试,发现很容易就中招儿了。在这里我会分享自己写的一个测试的例子来说明PHP中的session是如何不安全的,以及在项目中如何加强其安全性。
对于session的原理机制,网上有很多好的文章来介绍,我们可以自行查阅。下面直接分享测试用的例子。
这个测试的例子主要就是一个登录页,登录成功以后可以修改密码,就这样一个简单的功能。
界面如下

PHP中的session安全吗?

首先是在项目入口的地方使用函数 session_start() 开启了session。这样当客户端发起请求的时候,会产生一个身份标识 也就是 SessionID。通过cookie的方式保存在客户端,客户端和服务端每次的通信都是靠这个SessionID来进行身份识别的。
登录成功以后,会将 用户id、用户名存入session中

$_SESSION[‘userid'] = 用户id
$_SESSION[‘uname'] = 用户名

以后所有的操作都是通过判断 $_SESSION[‘userid']是否存在来检查用户是否登录。代码如下:

if(isset($_SESSION['userid'])) return true;

对于修改密码接口的调用是通过ajax  post的方式将数据传输到服务端的。

$.post("接口*******",
  {
     oldpass:oldpass,
     newpass:newpass,
     userid:uid,
  },
  function(data){
     data = eval('(' +data+ ')');
     $('.grant_info').html(infos[data.info]).show();
  }
);

注意,我这里将这段代码写在了html页面中,所以说如果看到了html代码,也就知道了接口地址了。
修改密码的接口是这样实现的,首先是判断用户是否登录,如果登录才会进行密码的修改操作。
测试例子的实现思路大概就是上面介绍的那样。
利用SessionID攻击
1. 首先是获取SessionID,当然攻击者获取此标识的方式有很多,由于我的水平有限,至于如何获取我在这里不做介绍。我们可以模拟一下,先正常访问此项目,然后通过浏览器查看SessionID,以此得到一个合法的用户标识。可以在请求头中看到此项ID

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Connection: keep-alive
Cookie: Hm_lvt_bf1154ec41057869fceed66e9b3af5e7=1450428827,1450678226,1450851291,1450851486; PHPSESSID=2eiq9hcpu3ksri4r587ckt9jt7;
Host: ******
Referer: ******
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:41.0) Gecko/20100101 Firefox/41.0

得到sessionID以后,如果此用户登录成功,那么服务端的session里就有此用户的信息了。
2. 获取到SessionID以后,假如攻击者已经知道修改密码的接口,就可以直接修改此用户的密码了。如果攻击者还没有得到接口地址,可以通过查看页面代码找出接口地址。可以使用如下的命令

#curl --cookie "PHPSESSID=2eiq9hcpu3ksri4r587ckt9jt7" 页面地址

上面我们说过,在此例子中ajax代码是写在html页面中的,所以在此页面可以查看到接口地址
部分html代码如下

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
……
var uid = $(".userid").val();
$.post("/User/User/modifypass_do",
     {
        oldpass:oldpass,
        newpass:newpass,
        userid:uid,
     },
    function(data){
      data = eval('(' +data+ ')');
      $('.grant_info').html(infos[data.info]).show();
    }
 );
……
<span><input type="password" name="oldpass" id="textfield_o" placeholder="原密码"></span>
<span><input type="password" name="newpass" id="textfield_n" placeholder="新密码"></span>
<span><input type="password" name="confirmpass" id="textfield_c" placeholder="确认密码"></span>
<input type="button" class="btn_ok" value="确认修改" />

3. 得到接口以后可以通过curl 模拟post发送数据来修改密码
命令如下

# curl --cookie "PHPSESSID=2eiq9hcpu3ksri4r587ckt9jt7" -d oldpass=111111 -d newpass=000000 -d userid=用户id 接口地址

如果此用户已经登录,那么攻击者可以通过执行以上命令修改用户的密码。
解决方法
对于以上方式的攻击,我们可以通过使验证方式复杂化来加强其安全性。其中一种方式就是利用请求头中的User-Agent项来加强其安全性

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Connection: keep-alive
Cookie: Hm_lvt_bf1154ec41057869fceed66e9b3af5e7=1450428827,1450678226,1450851291,1450851486; PHPSESSID=2eiq9hcpu3ksri4r587ckt9jt7;
Host: ******
Referer: ******
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:41.0) Gecko/20100101 Firefox/41.0

在项目开始的时候最初我们只是用了session_start()函数来开启session。现在我们可以在session_start() 下面 添加这段代码

$_SESSION[‘User_Agent'] = md5($_SERVER[‘HTTP_USER_AGENT']);

然后在每次判断是否登录的时候,添加判断条件如下

If(isset($_SESSION[‘userid']) && $_SESSION[‘User_Agent'] == md5($_SERVER[‘HTTP_USER_AGENT'])){
    return true;
}

这样就可以避免上述简单的攻击。
总结:
当然,实际情况中的攻击远非这么简单,首先在获取SessionID这一步就比较困难,然后就是和服务端交互的代码尽量加密,可以避免上述的情况。在我们第二次修改代码以后,可以增加攻击的复杂程度,并不能杜绝攻击。攻击的方式多种多样,这里只是一种简单的方式,仅提供一种思路,但是原理是一样的,在实际情况中可以根据实际情况增强我们代码的安全程度。

这里只是分享自己在工作中碰到的问题,权当抛砖引玉,希望大家可以进一步深入学习。

PHP 相关文章推荐
php 结果集的分页实现代码
Mar 10 PHP
一个PHP验证码类代码分享(已封装成类)
Jul 17 PHP
php地址引用(php地址引用的效率问题)
Mar 23 PHP
php中长文章分页显示实现代码
Sep 29 PHP
php魔术方法与魔术变量、内置方法与内置变量的深入分析
Jun 03 PHP
Drupal读取Excel并导入数据库实例
Mar 02 PHP
PHP5.5和之前的版本empty函数的不同之处
Jun 13 PHP
codeigniter上传图片不能正确识别图片类型问题解决方法
Jul 25 PHP
PHP中empty和isset对于参数结构的判断及empty()和isset()的区别
Nov 15 PHP
PHP实现实时生成并下载超大数据量的EXCEL文件详解
Oct 23 PHP
PHP设计模式之装饰器模式定义与用法详解
Apr 02 PHP
TP5框架页面跳转样式操作示例
Apr 05 PHP
PHP下载远程图片并保存到本地方法总结
Jan 22 #PHP
PHP连接MYSQL数据库实例代码
Jan 20 #PHP
CodeIgniter配置之autoload.php自动加载用法分析
Jan 20 #PHP
Twig模板引擎用法入门教程
Jan 20 #PHP
CodeIgniter控制器之业务逻辑实例分析
Jan 20 #PHP
CodeIgniter自定义控制器MY_Controller用法分析
Jan 20 #PHP
CodeIgniter钩子用法实例详解
Jan 20 #PHP
You might like
Yii框架中 find findAll 查找出制定的字段的方法对比
2014/09/10 PHP
Zend Guard使用指南及问题处理
2015/01/07 PHP
在jQuery中 关于json空对象筛选替换
2013/04/15 Javascript
判断滚动条到底部的JS代码
2013/11/04 Javascript
基于JQuery实现图片上传预览与删除操作
2016/05/24 Javascript
js实现带进度条提示的多视频上传功能
2020/12/13 Javascript
JS实现简单的星期格式转换功能示例
2018/07/23 Javascript
七行JSON代码把你的网站变成移动应用过程详解
2019/07/09 Javascript
vue中的循环对象属性和属性值用法
2020/09/04 Javascript
vue循环中点击选中再点击取消(单选)的实现
2020/09/10 Javascript
[11:01]2014DOTA2西雅图邀请赛 冷冷带你探秘威斯汀
2014/07/08 DOTA
[53:52]EG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
python中enumerate的用法实例解析
2014/08/18 Python
python3爬取各类天气信息
2018/02/24 Python
PyCharm代码格式调整方法
2018/05/23 Python
python脚本实现验证码识别
2018/06/07 Python
Python 进程之间共享数据(全局变量)的方法
2019/07/16 Python
Django中间件基础用法详解
2019/07/18 Python
python实现对服务器脚本敏感信息的加密解密功能
2019/08/13 Python
python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧(自定义信号与槽)详解与实例
2020/03/06 Python
Python实现封装打包自己写的代码,被python import
2020/07/12 Python
基于python实现操作redis及消息队列
2020/08/27 Python
吃透移动端 Html5 响应式布局
2019/12/16 HTML / CSS
英国儿童鞋和靴子:Start-Rite
2019/05/06 全球购物
美国浴缸、水槽和水龙头购物网站:Vintage Tub & Bath
2019/11/05 全球购物
捷克母婴用品购物网站:Feedo.cz
2020/12/28 全球购物
Auguste The Label官网:澳大利亚一家精品女装时尚品牌
2020/06/14 全球购物
.NET程序员的数据库面试题
2012/10/10 面试题
小学教师管理制度
2014/01/18 职场文书
化学教学随笔感言
2014/02/19 职场文书
《望庐山瀑布》教学反思
2014/04/22 职场文书
服务承诺书怎么写
2014/05/24 职场文书
家长会主持词开场白
2015/05/29 职场文书
第一节英语课开场白
2015/06/01 职场文书
创业计划书之DIY自助厨房
2019/09/06 职场文书
java中用float时,数字后面加f,这样是为什么你知道吗
2021/09/04 Java/Android