"短信轰炸机",是别人经过爬虫或者其余抓取手段在网路上收集那些公司平台短信业务接口的一个集成程序,可能只须要输入一个手机号,对方一成天都会收到各大平台的注册或提醒短信,就是手机在那里响个不停。由于如今手机只能对单方的多条短信进行屏蔽,而这种是多平台同时进行发送,很难拦截。php
而被他们收集接口的乙方公司,可能一天短信的消耗量就比过一周的量,形成公司财产损失。其实相比之前咱们遇到的被刷“提现”接口能够直接获取金钱,刷短信接口的行为,开始我非常想不通,就单纯为了消耗别人公司的短信费,他也得不到什么好处,为何要这样作。当时也没想对方是出于什么理由,只是先把短信接口从新整改,防止的手法在下面介绍。html
这种行为我也是最近才了解,原来网上也存在像周星驰电影"整蛊专家"的人,收取别人的钱开展对本身客户提供的人进行骚扰等业务,而短信就是其中一种,网上还有经过输入手机号,对方手机一天所有是陌生人来电的“呼死你”软件,心理承受力差的可能就抑郁,好点的话至少一天的心情都很差。前端
咱们公司如今短信的用处主要仍是注册/登录验证码,找回密码,绑银行卡,修改支付密码,因此一天的量并不大。但那一天充了钱,不出几分钟,直接刷到没有,还好发现的早,解决的也早。web
解决方法,在PC端能够在发短信验证码时,加上图形验证码校验,最好是要能和用户互动的,好比拖动或算术的,由于若是是静态的图形验证也不是很安全,毕竟如今的图片识别接口也不少。而在移动端,加图形验证体验不是很好,能够和前端经过约定密钥实现,而在H5上,就很尴尬了,并且还遇到一个只有必须绕弯路的事情。ajax
一,APP端apache
1.IP和手机号黑名单(ip的例子)json
<?php $Ip = $request->ip(); $Iplist = ['180.137.97.10']; if (in_array($Ip, $Iplist)) { return $this->sendError(1, '业务限流', 200); }
2.限制指定的user_agent后端
<?php $string = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0"; if ($Agent == $string) { return $this->sendError(1, '业务限流', 200); }
3.限制多久后才能发api
(1).获取指定键的一个缓存是否存在,存在则返回 $is_msgtime = cache($mobile.'_msgtimes'); if (!empty($is_mesgtime)) { return $this->sendError(1, '60秒后才能继续发', 200); } (2).在短信发送成功时,存下该键名,并缓存60秒(按需求指定时间) cache($mobile.'_msgtimes', 1, 60);
4.限制单号单日发送的个数限制跨域
(1)获取缓存指定键名的值为多少(number),并与最大限制比较 $Daytimes = intval(cache($mobile.'_daytimes')) ? intval(cache($mobile.'_daytimes')) : 0; if(Daytimes>3) { return ^^^^^^^^^ } (2)短信发送成功记录并累加该号该日发送量 if ($Daytimes > 0) { $nextday = strtotime("today") + 24 * 60 * 60; $expires = $nextday - time(); Cache::set($mobile.'_daytimes', $Daytimes + 1, $expires); } else { $nextday = strtotime("today") + 24 * 60 * 60; $expires = $nextday - time(); Cache::set($mobile.'_daytimes', 1, $expires); }
以上部分是没用,由于ip和手机号,user_agent均可以经过特殊手段伪造,而且上面只是对单号码进行拦截,因此问题就是别人经过不少号码对接口请求,这样同样的能够刷。
5.接口添加复杂性,增长签名请求等方式
(1).请求接口时,除了手机号参数,还需带上签名(一个不可逆加密方式处理的字符串,先后端约定一个key),和时间戳.
(2).后端根据约定的key和手机号也作相应的加密与传过来的签名匹配,并和时间戳和当前服务器时间作比较,时间戳最好也做为签名加密,加密方式多种,不论是DES,RSA仍是MD5看我的。
$timestamp = input('get.timestamp') ? input('get.timestamp') : 0; // 时间戳 $sign = input('get.app_sign') ? input('get.app_sign') : ''; // 请求签名 $terminal = input('get.term') ? input('get.term') : ''; // 终端标识 1.app 2.web $veryify_code = mt_rand(100000, 999999); // 6位数的验证码 $priKey = "paobuqianjinzhf..20181029"; // 加密秘钥 // 判断终端 if(!empty($terminal) && ($terminal == 'app')) { $signAuth = md5($priKey.$mobile.$timestamp); if($timestamp > time()+10 || $timestamp<time()-60*10) { return $this->sendError(1, '请求已过时', 200); } if($sign != $signAuth) { return $this->sendError(1, '签名匹配失败', 200); }
短语短信发送,最好用一张表记录一下,无论成功与否,之后查起来也方便,到底这接口发了哪些号码,何时发的等等
二,H5端
在h5上,上面的经过key撒盐的方式传参就行不通了,由于网页能够查看代码,若是把key放在前端传上来,能够看的很清楚。可是能够在后端生成一个串分配到网页请求的时候传上来就能够啊,就想表单令牌同样。可是咱们这个H5是纯静态,没有后台程序,接口都只是跨域ajax请求。因此分配字符串到页面行不通,经过接口请求签名也行不通,由于别人抓到了这些接口,仍是同样的能够经过同样的方式实现一遍。无奈只能经过图形验证码。
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2018/10/29 0029 * Time: 16:15 */ namespace Admin\Controller; use Think\Controller; use Think\Verify; // 验证码类 use Admin\Model; class RegisterController extends Controller // 控制器:用户登陆 { // 登录控制器的入口函数 public function index() { $this->redirect('Admin/Register/userRegister'); } // 用户注册界面 public function userRegister() { // $parm=I('get.'); $ajax = new AjaxController(); $this->assign('loginurl', $ajax->getAjaxUrl('ajaxuserloginexe')); $token=md5('paobuqianjinzhf..20181029'.time()); $this->assign('token',$token); $this->display('user_register'); } // 生成登录验证码 public function verfyCode() { $config = array( 'fontSize' => 50, // 验证码字体大小 'length' => 4, // 验证码位数 //'useNoise' => false, // 关闭验证码杂点 ); $Verify = new \Think\Verify($config); $Verify->entry(); } // 检测验证码 public function checkVerifyCode() { $code = I('code'); $mobile = I('mobile'); $term = 'app'; $timestamp =time(); $prikey='paobuqianjinzhf..20181029'; $app_sign = md5($prikey.$mobile.$timestamp); if (empty($mobile)){ $result_data['state'] = 0; $result_data['string'] = '手机号必填写'; returnjsontojs($result_data); } if(!check_verify($code,false)) { $result_data['state'] = 0; $result_data['string'] = '验证码不正确'; returnjsontojs($result_data); } $Agent = $_SERVER['HTTP_USER_AGENT']; // $url ="http://www.api.com/v1/ThirdParty/sendMsg?mobile=".$mobile.'&term='.$term.'&app_sign='.$app_sign.'×tamp='.$timestamp; $url ="https://api.runmoneyin.com/v1/ThirdParty/sendMsg?mobile=".$mobile.'&term='.$term.'&app_sign='.$app_sign.'×tamp='.$timestamp; // $url ="https://api.runmoneyin.com/v1/ThirdParty/sendMsg?mobile=".$mobile; $curl = curl_init(); //设置抓取的url curl_setopt($curl, CURLOPT_URL, $url); //设置头文件的信息做为数据流输出 curl_setopt($curl, CURLOPT_HEADER, 1); //设置获取的信息以文件流的形式返回,而不是直接输出。 // curl_setopt($curl,CURLOPT_USERAGENT,$Agent); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); //执行命令 $data = curl_exec($curl); $body=''; $result_data['state'] = false; if (curl_getinfo($curl, CURLINFO_HTTP_CODE) == '200') { $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE); $header = substr($data, 0, $headerSize); $body = json_decode(substr($data, $headerSize),true); } //关闭URL请求 curl_close($curl); if ($body['error']==0){ $result_data['state'] = true; } $result_data['string']=$body['message']; returnjsontojs($result_data); } }
以上我是在另外一个系统下放了那个H5页面,由于静态页面也没法作图形验证码,获取验证码时先判断图形验证码是否正确,正确再经过CURL请求真正的短信接口,跟APP同样的请求方式传参。
可是这个进行到后面却发现,咱们那个H5页面的地址已经作了固定域名二维码并打印了上万张,如今把H5的地址换掉的话,也就是域名变了,也就意味着那些打印的二维码全都没用了,因而想到一个办法: 在原来静态H5的地址,加上一个访问a.html会默认访问a.php,而后在a.php作一个跳转,跳转到咱们修改图形验证码的域名就能够了,这样仍是访问原来的地址,可是会重定向到新系统的地址。由于咱们只是对a.html起效,其余同目录的b.html不作处理,若是是在服务器上作重定向,整个域名下的都会被重定向,
1.在a.html目录下新建a.php,并新建一个.htaccess文件
2.对a.html的服务器能支持支持.htaccess
打开apache安装目录下的httpd.conf文件,把
AllowOverride None 改成AllowOverride All ,如图
去掉下面的注释 #
LoadModule rewrite_module modules/mod_rewrite.so,如图
3.htaccess文件修改成
<IFMODULE mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule (.*)\a.html$ $1\a.php [QSA,PT,L]
</IFMODULE>
我的公众号:ZERO开发