如何防止通过CURL频繁提交请求,后端处理
博客专区 > lock-li 的博客 > 博客详情
如何防止通过CURL频繁提交请求,后端处理
lock-li 发表于2年前
如何防止通过CURL频繁提交请求,后端处理
  • 发表于 2年前
  • 阅读 257
  • 收藏 1
  • 点赞 2
  • 评论 0

移动开发云端新模式探索实践 >>>   

今天一个同事,对现有的项目做了一个测试,发现一个很严重的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
粉丝 37
博文 73
码字总数 34394
作品 1
×
lock-li
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: