最近系统经常遇到广告机发的垃圾信息攻击。很多人想到的方法是增强验证码的难度,或者做一些问答来防止。但是这样在防止广告机的同时,对正常用户的使用和体验也造成很大困难。这里提供一个智能验证码的思路。
即:在短时间内成功使用验证码后,验证码使用频率记录+1。短时间内下次生成时,将自动增加长度和难度。终极变态验证码就是扭曲的中文即可。
大致代码如下:
建立一个c_iphistory数据表,用来记录验证码使用频率。
CREATE TABLE IF NOT EXISTS `c_iphistory` ( `id` int(11) NOT NULL AUTO_INCREMENT, `ip` varchar(255) NOT NULL, `num` int(11) NOT NULL, `lastdateline` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ip` (`ip`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='使用验证码的ip对应次数';
$freq = 60;//定义洪水攻击的频率 //检查是否已存储 $tmp = db::r("select * from c_iphistory where ip='$_G[ip]'"); $len = 2; $type = 0; if($tmp && $tmp['num'] > 0) { $num = $tmp['num']; $timeout = TS - $tmp['lastdateline']; $num -= floor($timeout / $freq);//洪水指数降多少级 if($num < 0) $num = 0; $len += $num; } if($len > 3){//倍增难度,长度2-5位 $type = floor($len / 4); if($type > 8) { $type = 8; $len -= 34; }else{ $len = $len % 4; $len += 2; } }else{ $len += 2; } $secode = s::rrand($len, $type);//下面就用$secode生成图片并记录到验证码库中以备验证即可。//************************函数部分****************************// /** * 随机数 @zairwolf * */ function rrand($len, $type = 7) {//1 - Number //2 - Lower Char //4 - Upper Char //8 - Chinese mt_srand((double)microtime() * 1000000); switch($type) { case 0: $charlist = '012'; break; case 1: $charlist = '0123456789'; break; case 2: $charlist = 'abcdefghijklmnopqrstuvwxyz'; break; case 3: $charlist = '0123456789abcdefghijklmnopqrstuvwxyz'; break; case 4: $charlist = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 5: $charlist = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 6: $charlist = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 7: $charlist = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 8://使用中文 global $Ccharlist; if(!$Ccharlist) $Ccharlist = file_get_contents("lib/disturbTxt.lib"); break; } $str = ''; if($type == 8) { $max = strlen($Ccharlist) / 3-1; for($i = 0; $i < $len; $i++) $str .= substr($Ccharlist, mt_rand(0, $max) * 3, 3); }else { $max = strlen($charlist) - 1; for($i = 0; $i < $len; $i++) $str .= $charlist[mt_rand(0, $max)]; } return $str; }
//在验证码检查确认正确之后,进行如下操作$freq = 60;//定义洪水攻击的频率 //检查是否已存储 $tmp = db::r("select * from c_iphistory where ip='$ip'"); if(!$tmp) { $s = array( 'ip' => $ip, 'num' => 1, 'lastdateline' => TS, ); db::i("insert into c_iphistory set ".sqlcol($s)); }else{//更新洪水攻击记录 $timeout = TS - $tmp['lastdateline']; if($timeout > $freq){//超过1分之前发的,已有记录-- $num = $tmp['num'] - 1; if($num < 0) $num = 0; $s = array( 'num' => $num, 'lastdateline' => TS, ); db::q("update c_iphistory set ".sqlcol($s)." where id='$tmp[id]'"); }else{ $num = $tmp['num'] + 1; $s = array( 'num' => $num, 'lastdateline' => TS, ); db::q("update c_iphistory set ".sqlcol($s)." where id='$tmp[id]'"); } }
暂时没验证代码 也没仔细看 不过思路和分享的精神要先表扬...
感谢分享。收藏了。
思路不错。 学习了。。
刚接触PHP,代码看不太懂。我的疑问是:这样使用数据库,对服务器会造成多大的压力?
想法不错,而且现在百度、新浪微博都是这么做的。
不过实际中可不能把这个值记录在mysql中,否则光这个查库和写库操作就够受的了。
可以在内存数据库中使用hash表来存储这个值,hash key可以用session_id。
感觉这样鸭梨有点大。 难道用数组存储?
想法不错,而且现在百度、新浪微博都是这么做的。
不过实际中可不能把这个值记录在mysql中,否则光这个查库和写库操作就够受的了。
可以在内存数据库中使用hash表来存储这个值,hash key可以用session_id。
n内存数据库是啥玩意儿?? 就是放在内存是不是??
好想法。~!
思路不错,收藏了,实现起来还不是那么容易说。思路不错,谢谢了。
这让我想起贴吧的那些验证码。