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 相关文章推荐
Apache2 httpd.conf 中文版
Dec 06 PHP
超级好用的一个php上传图片类(随机名,缩略图,加水印)
Jun 30 PHP
PHP Undefined index报错的修复方法
Jul 17 PHP
PHP 杂谈《重构-改善既有代码的设计》之三 重新组织数据
Apr 09 PHP
php下载文件的代码示例
Jun 29 PHP
有关PHP性能优化的介绍
Jun 20 PHP
php加密解密函数authcode的用法详细解析
Oct 28 PHP
微信支付开发动态链接Native支付
Jul 12 PHP
PHP身份证校验码计算方法
Aug 10 PHP
php检查函数必传参数是否存在的实例详解
Aug 28 PHP
PHP排序二叉树基本功能实现方法示例
May 26 PHP
CentOS7.0下安装PHP5.6.30服务的教程详解
Sep 29 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
正义联盟的终局之战《天启星战争》将成为DC动画宇宙的最后一部
2020/04/09 欧美动漫
phpfans留言版用到的install.php
2007/01/04 PHP
如何用C语言编写PHP扩展的详解
2013/06/13 PHP
php简单socket服务器客户端代码实例
2015/05/18 PHP
php 读写json文件及修改json的方法
2018/03/07 PHP
PHP读取Excel内的图片(phpspreadsheet和PHPExcel扩展库)
2019/11/19 PHP
PhpStorm的使用教程(本地运行PHP+远程开发+快捷键)
2020/03/26 PHP
PHP设计模式(一)工厂模式Factory实例详解【创建型】
2020/05/02 PHP
javascript 获取表单file全路径
2009/12/31 Javascript
jQuery Ajax方法调用 Asp.Net WebService 的详细实例代码
2011/04/27 Javascript
js分页工具实例
2015/01/28 Javascript
javascript实现动态显示颜色块的报表效果
2017/04/10 Javascript
Angularjs修改密码的实例代码
2017/05/26 Javascript
JS路由跳转的简单实现代码
2017/09/21 Javascript
angular 服务的单例模式(依赖注入模式下)详解
2018/10/22 Javascript
jQuery ajax仿Google自动提示SearchSuggess功能示例
2019/03/28 jQuery
[03:11]完美世界DOTA2联赛PWL DAY8集锦
2020/11/09 DOTA
浅谈python 四种数值类型(int,long,float,complex)
2016/06/08 Python
深入理解Python中的内置常量
2017/05/20 Python
Python语言实现百度语音识别API的使用实例
2017/12/13 Python
PySide和PyQt加载ui文件的两种方法
2019/02/27 Python
手把手教你使用Python创建微信机器人
2019/04/29 Python
pycharm通过anaconda安装pyqt5的教程
2020/03/24 Python
Python3.8.2安装包及安装教程图文详解(附安装包)
2020/11/28 Python
英国轻奢珠宝品牌:Astley Clarke
2016/12/18 全球购物
Ray-Ban雷朋瑞典官方网站:全球领先的太阳眼镜品牌
2019/08/22 全球购物
解决python 输出到csv 出现多空行的情况
2021/03/24 Python
前台接待的工作职责
2013/11/21 职场文书
在校实习生求职信
2014/06/18 职场文书
音乐教育专业自荐信
2014/09/18 职场文书
女生抽烟检讨书
2014/10/05 职场文书
2015元旦主持词开场白和结束语
2014/12/14 职场文书
莫言诺贝尔获奖感言(全文)
2015/07/31 职场文书
党员干部学法用法心得体会
2016/01/21 职场文书
2016年幼儿园万圣节活动总结
2016/04/05 职场文书
Python 处理表格进行成绩排序的操作代码
2021/07/26 Python