文档章节

如何防止通过CURL频繁提交请求,后端处理

lock-li
 lock-li
发布于 2016/05/11 16:47
字数 829
阅读 419
收藏 1

今天一个同事,对现有的项目做了一个测试,发现一个很严重的Bug。正常登录进来后,复制完整的cookie值,(我们是用cookie加密进行会话的)
来进行curl模拟其它操作,频繁的插入操作,最终导致表数据量大。

他和说我,对该表数据做一个最大条数限制。对于需求固定的来话,这是一个解决方法。对连续变动变态需求,这些方法往往不行。

这里先不说前端的防护,最终还是要归到后端处理检测。

最终想了个办法,

第一层防护
先给各个模块下的异步请求,做全局限制,只要是ajax异步请求的,才可以访问我们的功能方法。我们的代码结构是按各个功能模块,独自创建异步控制器,如用户模板,可能这样的“UserAjax.class.php”;
这样,我们可以在析构方法里添加一个方法。


public function __construct() {
	parent::__construct ();
	$this->common->isAjax();
}

假设isAjax在common通用方法中:

public function isAjax() {
	if (!isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
		$this->jsonmsg(array('code' => 0, 'msg' => '非法请求'));
	}
}

第二层防护:
对每个请求频率做限制,这个要写一下方法。方法的思路是这样的,对每个请求的IP进行会话保存,存储会话时间戳,再检验时间内规定的请求次数。
下面的方法同样写在common通用方法类中

a、获取IP

public function getIp() {
	if (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
		$ip = getenv('REMOTE_ADDR');
	} elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
		$ip = $_SERVER['REMOTE_ADDR'];
	} elseif (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
		$ip = getenv('HTTP_CLIENT_IP');
	} elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
		$ip = getenv('HTTP_X_FORWARDED_FOR');
	}
	$tmpIp = filter_var($ip, FILTER_VALIDATE_IP);
	$ip = $tmpIp ? $tmpIp : 'unknown';
	return $ip;
}

b、检测时间内请求次数,详情见方法注释

public function checkRequestNumber() {
	//获取IP
	$ip = $this->getIp();
	//请求会话的URI,组合后进行md5加密,因为我们通常一个请求,通过不同的参数调用不同的业务逻辑
	//如ajax_user?op=checkEmail,checkUsername	
	$key = md5($_SERVER['REQUEST_URI'].$_REQUEST['op']);
	
	$errMsg = '提交过于频繁,请稍后再试吧!';
	//IP时间栈数组
	if (!is_array($_SESSION[$ip][$key])) {
		$_SESSION[$ip][$key] = array();
	}
	
	if (isset($_SESSION[$ip][$key][0])) {
		$_SESSION[$ip][$key][] = time();
	
		//session保存时间为60秒,清空session
		$requestFir = time() - $_SESSION[$ip][$key][0];
		if ($requestFir > 60) {
			$_SESSION[$ip][$key] = array();
		}
		
		//两次提交小于1s禁止提交
		$requestSec = time() - $_SESSION[$ip][$key][count($_SESSION[$ip][$key]) - 3];
		if ($requestSec < 1) {
			$this->jsonmsg(array('code' => 0, 'msg' => $errMsg . 1));
		};
		
		//您在10s内已经提交了3请求,禁止提交
		$requestThi = time() - $_SESSION[$ip][$key][count($_SESSION[$ip][$key]) - 3];
		if (isset($_SESSION[$ip][$key][3]) && ($requestThi < 10)) {
			$this->jsonmsg(array('code' => 0, 'msg' => $errMsg . 2));
		}
		
		//您在1分钟期间已经提交了5请求,禁止提交
		$requestFif = time() - $_SESSION[$ip][$key][count($_SESSION[$ip][$key]) - 3];
		if (isset($_SESSION[$ip][$key][5]) && ($requestFif < 60)) {
			$this->jsonmsg(array('code' => 0, 'msg' => $errMsg . 3));
		}
	} else {
		$_SESSION[$ip][$key][] = time();	
	}
}

在基类中调用 checkRequestNumber(),代码如下:

if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
	$common->checkRequestNumber();
}

最后,你也可以对请求频繁的加图片验证码,提示用户操作请求频繁~

 

附上同事请求的CURL模拟小方法:

echo post();

function post($url = 'http://www.test.com/ajax_user', $post_data = '', $timeout = 5){

	$post_data = 'email='.mt_rand(10000,99999999).'@qq.com&op=addemail';
	$cookie = '........';
	$ch = curl_init();
	curl_setopt ($ch, CURLOPT_URL, $url);
	curl_setopt ($ch, CURLOPT_POST, 1);
	curl_setopt ($ch, CURLOPT_COOKIE, $cookie);
	if($post_data != ''){
		curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
	}
	curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
	curl_setopt($ch, CURLOPT_HEADER, false);
	$file_contents = curl_exec($ch);
	curl_close($ch);
	return $file_contents;
}

 

© 著作权归作者所有

共有 人打赏支持
lock-li

lock-li

粉丝 40
博文 73
码字总数 34522
作品 1
松江
CTO(技术副总裁)
加载中

评论(1)

l
lanmanck
你这个有问题,curl每次都没有带cookie,导致每次session重置,你的session判断是无效的,除非带了cookie,或者把ip放到数据库
聚合数据API查询快递数据-短信验证码-企业核名

有位朋友让我给他新开的网站帮忙做几个小功能,如下: 输入快递公司、快递单号,查询出这个快件的所有动态(从哪里出发,到了哪里) 在注册、登录等场景下的手机验证码(要求有一定的防刷策略...

熊babi
2016/08/30
21
0
180730-Spring之RequestBody的使用姿势小结

Spring之RequestBody的使用姿势小结 SpringMVC中处理请求参数有好几种不同的方式,如我们常见的下面几种 根据 对象获取 根据 注解获取url参数 根据 注解获取请求参数 根据Bean的方式获取请求...

小灰灰Blog
07/30
0
0
利用redis缓存解决高并发下后端重复请求措施

最近在进行压力测试的时候发现在高并发下,有些接口很可能因为重复请求导致对数据库操作出来的数据不是你想要的那个样子。比如,用户签到,你只想让用户一天签到一次,为了防止签到多次,你对...

xiaomin0322
04/13
0
0
如何查看nginx状态?如何统计访问量?

nginx是个什么玩意儿本文不再多说,直奔主题! 1. 如何查看nginx的状态? stub_status 模块介绍 stub_status 模块主要用于查看Nginx的一些状态信息. 本模块默认是不会编译进Nginx的,如果你要...

蜗牛的嘲讽
2016/12/19
0
0
使用聚合数据API查询快递数据-短信验证码-企业核名

文章来源:http://www.cnblogs.com/annie00/p/5810326.html 有位朋友让我给他新开的网站帮忙做几个小功能,如下: 输入快递公司、快递单号,查询出这个快件的所有动态(从哪里出发,到了哪里...

熊babi
2016/08/30
815
1

没有更多内容

加载失败,请刷新页面

加载更多

HashMap工作原理及实现

HashMap工作原理及实现 1. 概述 什么时候会使用HashMap?他有什么特点? 知道HashMap的工作原理吗? 知道get和put的原理吗? 知道hash的实现吗?为什么要这样实现? 如果HashMap的大小超过了...

傅小水water
16分钟前
0
0
swagger如何屏蔽某些接口,不对外公开--使用@ApiIgnore

@ApiIgnore@RestController@RequestMapping(value = "/i18nTest")public class I18nTestController {// @Resource// private LocaleMessageSourceService localeMessageSourceSe......

karma123
20分钟前
0
0
大数据技术学习,大数据处理为何选择Spark,而不是Hadoop

大数据处理为何选择Spark,而不是Hadoop。 一.基础知识 1.Spark Spark是一个用来实现快速而通用的集群计算的平台。 在速度方面,Spark扩展了广泛使用的MapReduce计算模型,而且高效地支持更多...

董黎明
27分钟前
0
0
Java程序员的书籍清单

我自己一路来,看过的书籍,都是经典,我自己也在不断地回溯,在这里分享给大家(排名不分先后,这里仅仅是我自己购买这些书籍的时间顺序排列): 《高效程序员的45个习惯 敏捷开发修炼之道》...

ChinaHYF
53分钟前
1
0
DeepMind 开源图神经网络的代码

用于支持论文《Relational inductive biases, deep learning, and graph networks》。 github A graph network takes a graph as input and returns a graph as output. The input graph has......

shengjuntu
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部