防刷短信的简单机制(不使用第三方行为验证)
作者:互联网
前言
网站短信接口发送使用url请求,容易被攻击。
目前第三方的行为验证码,防御效果应该是最好的,像国内极验、顶象,国外google做的最牛掰,勾选按钮“I'm not a robot”自动区别人机操作。
像ip限制、发送次数限制本文不再说明,目前机器太容易绕过了
机制原理
主要通过增加请求参数的有限性的验证
1.参数name有效性的验证
2.参数value有效性的验证
前台假设的注册页面
<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和curMin
$smsRandNum = mt_rand(100000,999999);
Session::set('sms_rand_num',$smsRandNum);
2.后端生成tokenName和tokenValue
$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和tokenValue1
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;
}
标签:code,短信,token,防刷,date,smRandNum,第三方,minRand,md5 来源: https://www.cnblogs.com/sentangle/p/11480829.html