網站短信接口發送使用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,後端代碼經過一樣加密方式進行匹配驗證
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; }