键盘上的"整蛊专家",如何防止短信轰炸机

 "短信轰炸机",是别人通过爬虫或者其他抓取手段在网路上收集那些公司平台短信业务接口的一个集成程序,可能只需要输入一个手机号,对方一整天都会收到各大平台的注册或提醒短信,就是手机在那里响个不停。因为现在手机只能对单方的多条短信进行屏蔽,而这种是多平台同时进行发送,很难拦截。

    而被他们收集接口的乙方公司,可能一天短信的消耗量就比过一周的量,造成公司财产损失。其实相比以前我们遇到的被刷“提现”接口可以直接获取金钱,刷短信接口的行为,开始我很是想不通,就单纯为了消耗别人公司的短信费,他也得不到什么好处,为什么要这样做。当时也没想对方是出于什么理由,只是先把短信接口重新整改,防止的手法在下面介绍。

    这种行为我也是最近才了解,原来网上也存在像周星驰电影"整蛊专家"的人,收取别人的钱开展对自己客户提供的人进行骚扰等业务,而短信就是其中一种,网上还有通过输入手机号,对方手机一天全部是陌生人来电的“呼死你”软件,心理承受力差的可能就抑郁,好点的话至少一天的心情都不好。

     我们公司现在短信的用处主要还是注册/登陆验证码,找回密码,绑银行卡,修改支付密码,所以一天的量并不大。但那一天充了钱,不出几分钟,直接刷到没有,还好发现的早,解决的也早。

    解决方法,在PC端可以在发短信验证码时,加上图形验证码校验,最好是要能和用户互动的,比如拖动或算术的,因为如果是静态的图形验证也不是很安全,毕竟现在的图片识别接口也很多。而在移动端,加图形验证体验不是很好,可以和前端通过约定密钥实现,而在H5上,就很尴尬了,而且还遇到一个只有必须绕弯路的事情。

一,APP端

1.IP和手机号黑名单(ip的例子)

<?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.限制多久后才能发

(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 AdminController;

use ThinkController;
use ThinkVerify;       // 验证码类
use AdminModel;

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 ThinkVerify($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.'&timestamp='.$timestamp;
        $url ="https://api.runmoneyin.com/v1/ThirdParty/sendMsg?mobile=".$mobile.'&term='.$term.'&app_sign='.$app_sign.'&timestamp='.$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$ $1a.php [QSA,PT,L]

</IFMODULE>

    个人公众号:ZERO开发

原文地址:https://www.cnblogs.com/zerofc/p/9940336.html