PHP基于timestamp和nonce实现的防止重放攻击方案分析


Posted in PHP onJuly 26, 2019

本文实例讲述了PHP基于timestamp和nonce实现的防止重放攻击方案。分享给大家供大家参考,具体如下:

以前总是通过timestamp来防止重放攻击,但是这样并不能保证每次请求都是一次性的。今天看到了一篇文章介绍的通过nonce(Number used once)来保证一次有效,感觉两者结合一下,就能达到一个非常好的效果了。

重放攻击是计算机世界黑客常用的攻击方式之一,所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程。

首先要明确一个事情,重放攻击是二次请求,黑客通过抓包获取到了请求的HTTP报文,然后黑客自己编写了一个类似的HTTP请求,发送给服务器。也就是说服务器处理了两个请求,先处理了正常的HTTP请求,然后又处理了黑客发送的篡改过的HTTP请求。

基于timestamp的方案

每次HTTP请求,都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。因为一次正常的HTTP请求,从发出到达服务器一般都不会超过60s,所以服务器收到HTTP请求之后,首先判断时间戳参数与当前时间相比较,是否超过了60s,如果超过了则认为是非法的请求。

假如黑客通过抓包得到了我们的请求url:
http://koastal.site/index/Info?uid=ZX07&stime=1480862753&sign=80b886d71449cb33355d017893720666

其中

$sign=md5($uid.$token.$stime);
// 服务器通过uid从数据库中可读出token

一般情况下,黑客从抓包重放请求耗时远远超过了60s,所以此时请求中的stime参数已经失效了。
如果黑客修改stime参数为当前的时间戳,则sign参数对应的数字签名就会失效,因为黑客不知道token值,没有办法生成新的数字签名。

但这种方式的漏洞也是显而易见的,如果在60s之内进行重放攻击,那就没办法了,所以这种方式不能保证请求仅一次有效。

基于nonce的方案

nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不同,所以该参数一般与时间戳有关,我们这里为了方便起见,直接使用时间戳的16进制,实际使用时可以加上客户端的ip地址,mac地址等信息做个哈希之后,作为nonce参数。
我们将每次请求的nonce参数存储到一个“集合”中,可以json格式存储到数据库或缓存中。
每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,如果存在则认为是非法请求。

假如黑客通过抓包得到了我们的请求url:
http://koastal.site/index/Info?uid=ZX07&nonce=58442c21&sign=80b886d71449cb33355d017893720666

其中

$sign=md5($uid.$token.$nonce);
// 服务器通过uid从数据库中可读出token

nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。
nonce参数作为数字签名的一部分,是无法篡改的,因为黑客不清楚token,所以不能生成新的sign。

这种方式也有很大的问题,那就是存储nonce参数的“集合”会越来越大,验证nonce是否存在“集合”中的耗时会越来越长。我们不能让nonce“集合”无限大,所以需要定期清理该“集合”,但是一旦该“集合”被清理,我们就无法验证被清理了的nonce参数了。也就是说,假设该“集合”平均1天清理一次的话,我们抓取到的该url,虽然当时无法进行重放攻击,但是我们还是可以每隔一天进行一次重放攻击的。而且存储24小时内,所有请求的“nonce”参数,也是一笔不小的开销。

基于timestamp和nonce的方案

那我们如果同时使用timestamp和nonce参数呢?
nonce的一次性可以解决timestamp参数60s的问题,timestamp可以解决nonce参数“集合”越来越大的问题。

我们在timestamp方案的基础上,加上nonce参数,因为timstamp参数对于超过60s的请求,都认为非法请求,所以我们只需要存储60s的nonce参数的“集合”即可。

假如黑客通过抓包得到了我们的请求url:
http://koastal.site/index/Info?uid=ZX07&stime=1480862753&nonce=58442c21&sign=80b886d71449cb33355d017893720666

其中

$sign=md5($uid.$token.$stime.$nonce);
// 服务器通过uid从数据库中可读出token

如果在60s内,重放该HTTP请求,因为nonce参数已经在首次请求的时候被记录在服务器的nonce参数“集合”中,所以会被判断为非法请求。超过60s之后,stime参数就会失效,此时因为黑客不清楚token的值,所以无法重新生成签名。

综上,我们认为一次正常的HTTP请求发送不会超过60s,在60s之内的重放攻击可以由nonce参数保证,超过60s的重放攻击可以由stime参数保证。

因为nonce参数只会在60s之内起作用,所以只需要保存60s之内的nonce参数即可。

我们并不一定要每个60s去清理该nonce参数的集合,只需要在新的nonce到来时,判断nonce集合最后一次修改时间,超过60s的话,就清空该集合,存放新的nonce参数集合。其实nonce参数集合可以存放的时间更久一些,但是最少是60s。
随机数集合可以根据业务场景采用定期清理或根据大小自动清理的方案,例如该接口每秒的请求数最高为1000,则60s内的请求数量最多为1500*60=90000,则我们在每次请求后检查集合大小是否超过90000,若超高该数量则清空。

验证流程

//判断stime参数是否有效
if( $now - $stime > 60){
  die("请求超时");
}
//判断nonce参数是否在“集合”已存在
if( in_array($nonce,$nonceArray) ){
  die("请求仅一次有效");
}
//验证数字签名
if ( $sign != md5($uid.$token.$stime.$nonce) ){
  die("数字签名验证失败");
}
/*
if( $now - $nonceArray->lastModifyTime > 60 ){
  $nonceArray = null;
}
$nonceArray.push($nonce);
*/
//处理随机数
$key = 'nonce'+$uid;
if($redis->sismember($key,$nonce) === true){
  die('拒绝重放攻击请求');
}
if($redis->scard($key) > 90000){
  $redis->del($key);
}
$redis->sadd($key,$nonce);
//重放攻击检查完成

参考文章:

http://www.360doc.com/content/14/0116/16/834950_345740386.shtml

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
用PHP编程开发“虚拟域名”系统
Oct 09 PHP
Ha0k 0.3 PHP 网页木马修改版
Oct 11 PHP
Memcache 在PHP中的使用技巧
Feb 08 PHP
探讨php中防止SQL注入最好的方法是什么
Jun 10 PHP
PHP取整函数:ceil,floor,round,intval的区别详细解析
Aug 31 PHP
php使用curl发送json格式数据实例
Dec 17 PHP
PHP APC配置文件2套和参数详解
Jun 11 PHP
PHP中error_log()函数的使用方法
Jan 20 PHP
php备份数据库类分享
Apr 14 PHP
基于CakePHP实现的简单博客系统实例
Jun 28 PHP
PHP面向对象程序设计子类扩展父类(子类重新载入父类)操作详解
Jun 14 PHP
PHP7.3.10编译安装教程
Oct 08 PHP
YII2.0框架行为(Behavior)深入详解
Jul 26 #PHP
php使用socket调用http和smtp协议实例小结
Jul 26 #PHP
php使用curl模拟多线程实现批处理功能示例
Jul 25 #PHP
yii框架使用分页的方法分析
Jul 25 #PHP
php实现的生成排列算法示例
Jul 25 #PHP
Yii框架中使用PHPExcel的方法分析
Jul 25 #PHP
PHP保留两位小数的几种方法
Jul 24 #PHP
You might like
PHP中数组合并的两种方法及区别介绍
2012/09/14 PHP
用php简单实现加减乘除计算器
2014/01/06 PHP
php基于str_pad实现卡号不足位数自动补0的方法
2014/11/12 PHP
php把文件设置为插件的技巧方法
2020/02/03 PHP
javascript预览上传图片发现的问题的解决方法
2010/11/25 Javascript
JavaScript中使用ActiveXObject操作本地文件夹的方法
2014/03/28 Javascript
jquery实现右侧栏菜单选择操作
2016/03/04 Javascript
关于meta viewport中target-densitydpi属性详解(推荐)
2017/08/18 Javascript
JS实现常见的查找、排序、去重算法示例
2018/05/21 Javascript
Node.js Buffer模块功能及常用方法实例分析
2019/01/05 Javascript
jQuery实现的网站banner图片无缝轮播效果完整实例
2019/01/28 jQuery
基于JavaScript判断两个对象内容是否相等
2020/01/10 Javascript
微信小程序canvas实现签名功能
2021/01/19 Javascript
Python3.6简单操作Mysql数据库
2017/09/12 Python
Python numpy 常用函数总结
2017/12/07 Python
Python中最大最小赋值小技巧(分享)
2017/12/23 Python
Python操作word常见方法示例【win32com与docx模块】
2018/07/17 Python
Python Pandas批量读取csv文件到dataframe的方法
2018/10/08 Python
python生成九宫格图片
2018/11/19 Python
python+selenium定时爬取丁香园的新型冠状病毒数据并制作出类似的地图(部署到云服务器)
2020/02/09 Python
Django如何使用jwt获取用户信息
2020/04/21 Python
python 如何快速复制序列
2020/09/07 Python
美国伊甸园兄弟种子公司:Eden Brothers
2018/07/01 全球购物
万豪国际住宅与别墅集团:Homes & Villas by Marriott International
2020/10/08 全球购物
写好自荐信要注意的问题
2013/11/10 职场文书
药学专业个人自我评价
2013/11/11 职场文书
禁止高声喧哗的标语
2014/06/11 职场文书
我的中国梦演讲稿小学篇
2014/08/19 职场文书
校园新闻广播稿5篇
2014/10/10 职场文书
销售辞职信范文
2015/03/02 职场文书
2016年国庆节宣传标语
2015/11/25 职场文书
oracle DGMGRL ORA-16603报错的解决方法(DG Broker)
2021/04/06 Oracle
Python获取指定日期是"星期几"的6种方法
2022/03/13 Python
Python借助with语句实现代码段只执行有限次
2022/03/23 Python
安装harbor作为docker镜像仓库的问题
2022/06/14 Servers
Python创建SQL数据库流程逐步讲解
2022/09/23 Python