[原]防刷短信的簡單機制(不使用第三方行爲驗證)

需求

網站短信接口發送使用url請求,容易被攻擊。php

目前第三方的行爲驗證碼,防護效果應該是最好的,像國內極驗、頂象,國外google作的最牛掰,勾選按鈕「I'm not a robot」自動區別人機操做。後端

像ip限制、發送次數限制本文再也不說明,目前機器太容易繞過了session

機制原理

主要經過增長請求參數的有限性的驗證函數

1.參數name有效性的驗證
2.參數value有效性的驗證

前臺假設的註冊頁面post

<form action='' method='post'>
    <dd>手 機 號<input name='mobile' placeholder="填寫正確的手機號碼" ></dd>
    <dd>設 置 密 碼<input type="password" name='password' placeholder="填寫登陸密碼" ></dd>
    <dd>驗 證 碼<input type="text" name='captcha'  placeholder="填寫右邊圖片所示的字符"><img src='' /></dd>
    <dd>手機驗證碼<input name="mcode" type="text"><button onclick="getcode();">獲取驗證碼</button></dd>
    <dd><input class="btn" type="submit" value="注 冊"></dd>
</form>

具體實現網站

1.頁面加載,後端隨機生成randNum(session存儲,設置有效時間爲1分鐘),生成當前分鐘curMin,根據curMin不一樣值分別經過不一樣加密方式(randNum和curMin參入運算)生成頁面tokenName, 經過加密方式(tokenName)生成tokenValue。
tokenName、tokenValue、curMin 傳遞前臺頁面,他們的有效性時間爲1分鐘

2.點擊發送請求,前臺js根據curMin和用戶輸入手機mobile 經過加密方式生成前臺tokenName1和tokenValue1

3.點擊發送請求js方法中,請求url傳入tokenName=tokenValue 和tokenName1=tokenValue2,後端代碼經過一樣加密方式進行匹配驗證

代碼實現(php)

1.後端隨機生成randNum和curMinthis

$smsRandNum = mt_rand(100000,999999);
Session::set('sms_rand_num',$smsRandNum);

2.後端生成tokenName和tokenValuegoogle

$this->currentMinute = substr(date("i",time()-60*5),1,1);
$tokenValue = $this->createSmsToken($this->currentMinute);
$tokenName = "a".md5($tokenValue)

createSmsToken函數加密

private function createSmsToken($minRand)
{
    $token = "";
    //時間隨機碼
    $smRandNum = Session::get('sms_rand_num');
    switch($minRand)
    {
        case 0:
            $token = md5($minRand.$smRandNum.date("Y"));break;
        case 1:
            $token = md5($minRand.$smRandNum.date("m"));break;
        case 2:
            $token = md5($minRand.$smRandNum.date("d"));break;
        case 3:
            $token = md5($minRand.$smRandNum.date("H"));break;
        case 4:
            $token = md5($minRand.$smRandNum.date("Y-m"));break;
        case 5:
            $token = md5($minRand.$smRandNum.date("Y-d"));break;
        case 6:
            $token = md5($minRand.$smRandNum.date("Y-H"));break;
        case 7:
            $token = md5($minRand.$smRandNum.date("m-d"));break;
        case 8:
            $token = md5($minRand.$smRandNum.date("m-H"));break;
        case 9:
            $token = md5($minRand.$smRandNum.date("d-H"));break;
        default:
            $token = md5($minRand.$smRandNum.date("Y-m-d"));break;
    }
    return $token;
}

3.生成前臺tokenName1和tokenValue1url

var tokenName1 = createToken(mobile, randNumber);
var tokenName1Val = sha1(tokenName1);

js函數createToken

function createToken(code, randNumber) {
    var c = String.fromCharCode(code.charCodeAt(0) + code.length + randNumber);
    for (var i = 1; i < code.length; i++) {
        c += String.fromCharCode(code.charCodeAt(i) + code.charCodeAt(i - 1) + randNumber);
    }
    c = escape(c);
    // 去掉特殊字符
    c = c.replace(/[\@\#\$\%\^\&\*\{\}\:\"\L\<\>\?]/, '');
    return ("b" + c);
}

js函數sha1

function sha1(s) {
    var data = new Uint8Array(encodeUTF8(s))
    var i, j, t;
    var l = ((data.length + 8) >>> 6 << 4) + 16,
        s = new Uint8Array(l << 2);
    s.set(new Uint8Array(data.buffer)), s = new Uint32Array(s.buffer);
    for (t = new DataView(s.buffer), i = 0; i < l; i++) s[i] = t.getUint32(i << 2);
    s[data.length >> 2] |= 0x80 << (24 - (data.length & 3) * 8);
    s[l - 1] = data.length << 3;
    var w = [],
        f = [

        function() {
            return m[1] & m[2] | ~m[1] & m[3];
        }, function() {
            return m[1] ^ m[2] ^ m[3];
        }, function() {
            return m[1] & m[2] | m[1] & m[3] | m[2] & m[3];
        }, function() {
            return m[1] ^ m[2] ^ m[3];
        }],
        rol = function(n, c) {
            return n << c | n >>> (32 - c);
        },
        k = [1518500249, 1859775393, -1894007588, -899497514],
        m = [1732584193, -271733879, null, null, -1009589776];
    m[2] = ~m[0], m[3] = ~m[1];
    for (i = 0; i < s.length; i += 16) {
        var o = m.slice(0);
        for (j = 0; j < 80; j++)
        w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1), t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0, m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
        for (j = 0; j < 5; j++) m[j] = m[j] + o[j] | 0;
    };
    t = new DataView(new Uint32Array(m).buffer);
    for (var i = 0; i < 5; i++) m[i] = t.getUint32(i << 2);

    var hex = Array.prototype.map.call(new Uint8Array(new Uint32Array(m).buffer), function(e) {
        return (e < 16 ? "0" : "") + e.toString(16);
    }).join("");
    return hex;
}

function encodeUTF8(s) {
    var i, r = [], c, x;
    for (i = 0; i < s.length; i++)
    if ((c = s.charCodeAt(i)) < 0x80) r.push(c);
    else if (c < 0x800) r.push(0xC0 + (c >> 6 & 0x1F), 0x80 + (c & 0x3F));
    else {
        if ((x = c ^ 0xD800) >> 10 == 0) //對四字節UTF-16轉換爲Unicode
        c = (x << 10) + (s.charCodeAt(++i) ^ 0xDC00) + 0x10000, r.push(0xF0 + (c >> 18 & 0x7), 0x80 + (c >> 12 & 0x3F));
        else r.push(0xE0 + (c >> 12 & 0xF));
        r.push(0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
    };
    return r;
}

4.後端匹配驗證

請求url示例

http://www.xxx.com/action=sendcode&mobile=18306561234&captcha=cihom&a386b4aad651fddec12bb7fdaae92c73dcf189a11=2c911c34936903f93fd766e55b8707c0&bAnphkpplfjp=d77c9f6debeb7904b64daec6872ab6f8d7fce4f7

後端驗證

$minRand = $this->currentMinute;
$tokenName1 = $this->createSmsTokenTwo($mobile,$minRand);
$tokenName1Val = sha1($tokenName1)

private function createSmsTokenTwo($code,$randNumber)
{
    $code = array_values(unpack('n*', iconv('utf-8', 'ucs-2', $code)));
    $c[] = $code[0] + count($code)+$randNumber;
    for ($i = 1; $i < count($code); $i ++)
    {
        $c[] = $code[$i] + $code[$i - 1]+$randNumber;
    }
    $r = '';
    foreach ($c as $v)
    {
        if ($v < 256)
            $r .= urlencode(chr($v));
        else
           $r .= '%u' . strtoupper(dechex($v));
    }
    $pattern = array(
        '/\@/',
        '/\#/',
        '/\$/',
        '/\%/',
        '/\^/',
        '/\&/',
        '/\*/',
        '/\{/',
        '/\}/',
        '/\:/',
        '/\"/',
        '/\</',
        '/\>/',
        '/\?/'
        
    );
    $r = preg_replace( $pattern , '', $r);
    return "b".$r;
}
相關文章
相關標籤/搜索