"短信轟炸機",是別人經過爬蟲或者其餘抓取手段在網路上收集那些公司平臺短信業務接口的一個集成程序,可能只須要輸入一個手機號,對方一成天都會收到各大平臺的註冊或提醒短信,就是手機在那裏響個不停。由於如今手機只能對單方的多條短信進行屏蔽,而這種是多平臺同時進行發送,很難攔截。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開發