彻底解决打开多页面时验证码出错的问题


一个古老的问题:有时你同时打开一个网站的多个页面,它们都要求输入验证码,而你输入验证码明明正确,然而却提示你错误——这就是著名的打开多页面时验证码出错的问题

为什么会产生这个问题?很简单,先来看验证码机制的原理
//图片端:
$num = mt_rand(1000, 9999); //生成随机数

@session_start();
$_SESSION['IMGCODE'] = $num; //存入session

$img = imagecreate(90,20); //用GD库创建图片
$black = imagecolorallocate($img, 0, 0, 0);
imagestring($img, 1, 5, 0, $num, $black); //将随机数打印在图片上
header("Content-type: image/gif");
imagegif($img); //输出图片
imagedestroy($img); //释放内存

//验证端:
if ($_POST['imgcode'] != $_SESSION['IMGCODE']) { //如果用户输入的验证码和session中的不一
die("验证码错误"); //终止脚本
}

很明显,当服务器产生验证码图片并发送给浏览器时这个验证码图片对应的数字就存在了session中,而session中这个IMGCODE的值是唯一的,因而打开多个带有验证码的页面时session中最后存储的是最后打开那个网页中的验证码数字,这是你在前面的页面中输入图片上的验证码然后提交就会出错了。

这样的出错带来的用户体验极糟,腾讯的WebQQ就忽略了这个问题,我一个同学有多个QQ号,于是经常打开一堆WebQQ登陆窗口,然后郁闷的发现提交时总是验证码出错。

如何解决?一个绕开这个问题的方法就是DedeCMS论坛上的一个方法:当浏览者填写提交表单时再载入验证码,这样的确聪明地绕过了问题,而且减轻了服务器负担,不过,为了减少评论填写者浪费的时间、也为了避免浏览者在多个页面同时填写评论表单导致错误依旧,我宁愿采取更好的方法:对每个实例采用一个独一无二的hiddencode来解决问题。
//前台
$hiddencode = md5(microtime().$_SERVER['REQUEST_URI"]);
echo '';
echo '';

//图片端的session写入部分改为:
$hiddencode = $_GET['hiddencode'];
@session_start();
$_SESSION['IMGCODE'.$hiddencode] = $num; //存入session

//验证端改为
$hiddencode = $_POST['hiddencode'];
if ($_POST['imgcode'] != $_SESSION['IMGCODE'.$hiddencode]) { //如果用户输入的验证码和session中的不一
die("验证码错误");
}


这样就明了地解决了问题。

当然,这里的$hiddencode变量是什么都行,只要能保证唯一性,比如md5(microtime())也行,mt_rand(0,10000)问题也不大……

据此做成WP插件也又要有一些改变,我已据此做出改进版的WP-imgcode插件,可以下载

评论

nortan 2011-05-26 11:30:15
你所提的方法与不使用验证码就是一样了,比如我想在你的网站的发垃圾信息,我专门写一个发帖子机,只要我取得你这个hidecode,就要以直接发了,实际你这种机制就是常用的token,这种有一定的用处,对专门写程序的人来说用处不大
kmxz 2011-06-09 19:33:49
@nortan

谢谢来访。这个hiddencode只是用来标记特定验证码的,验证码的内容是存在服务器端的session里的。