什么是验证码?
验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写。大名鼎鼎的图灵测试,通过一个人能答得上来,而机器不能正确回答的问题(五花八门形式的问题都有。。。),用以自动区分计算机和人。偶尔有不是通过图片,而是短信验证码来验证。
验证码的缺点也是显而易见的,破坏了用户体验(盯着看了好几遍还是傻傻分不清楚或者验证码图片显示不出来),所以需要不断在用户体验和抵挡机器OCR(光学字符识别)之间寻找平衡点。
* 像手机号注册,需要填写身份证号之类的手段,也都附带有类似的限制机器自动提交的目的。
为什么要用验证码?主要防范哪些方面?
验证码作为一种抵挡机器人手段,用来阻止各种批量暴力提交行为。
主要防范:
创新用法
除了用来反机器人程序,谷歌公司还将大数据概念发扬光大,在09年收购了验证码公司reCAPTCHA,将古籍里面不确定的字和确定的字合成成验证码图片。当大量用户识别确认确定的那个字之后,通过统计排序就能得到不确定的那个字最有可能的对应文字,从而“免费”利用了填写验证码的资源。自后又将这个技术发扬光大,把谷歌街景里不能识别的商铺和路标也放在验证码里识别。
防止OCR的方法
当天还是蓝的时候,CAPTCHA 也就是一个干干净净的字符串图片。后来就:
辅助视听
当验证码太难看清(字符分割不好,变形太厉害,显示屏太烂等),用户就需要使用耳朵听的方法得到验证码。一般来说,可以在服务器端根据生成的字符串,动态合成一段MP3,传送到客户端进行播放。
简单的编程实现方法介绍
<?php if(!isset($_SESSION)){ session_start(); } class captcha { private $width = 60; private $height = 25; private $length = 4; private $code; private $img; private $ttf = '/volume1/web/captcha/simsunb.ttf'; //初始化对象,需要传入字体文件位置 public function __construct($ttf){ $this->code = $this->genCode($this->length); $this->ttf = $ttf; } //可以自定义验证框大小,字符数量 public function setSize($width,$height,$length){ $this->width = $width; $this->height = $height; $this->length = $lenght; $this->code = $this->genCode($length); } //产生随机字符串 private function genCode($length){ $pattern = '1234567890ABCDEFGHIJKLOMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; $patternLength = strlen($pattern); for($i=0;$i<$length;$i++) { $key .= $pattern{mt_rand(0,$patternLength-1)}; } return $key; } //获取随机字符串,用于传送到前端,进行验证(通常还会进一步md5加密) public function getCode(){ return $this->code; } //产生验证图片,如果继承captcha类,则可以复写该函数,添加额外的噪声方法 public function genImg($pointNum,$lineNum){ $this->initialImg(); $this->writeString($this->img,$this->code,$this->ttf); $this->writePoints($this->img,$pointNum); $this->writeLines($this->img,$lineNum); } //以png格式输出图片 public function outputImg(){ ob_clean(); header('Content-type:image/png'); imagepng($this->img); } //初始化验证图片背景 private function initialImg(){ $img=imagecreate($this->width,$this->height); $bgcolor=imagecolorallocate($img,240,240,240); $rectangelcolor=imagecolorallocate($img,150,150,150); imagerectangle($img,1,1,$this->width-1,$this->height-1,$rectangelcolor); $this->img = $img; } //写入字符串 private function writeString($img,$code,$ttf){ $length = strlen($code); for($i=0;$i<$length;$i++){ $codecolor=imagecolorallocate($img,mt_rand(50,200),mt_rand(50,128),mt_rand(50,200)); $angle=rand(-30,30); $charx=$i*(($this->width-10)/$length)+8; $chary=($this->height+14)/2+rand(-1,1); imagettftext($img,15,$angle,$charx,$chary,$codecolor,$ttf,$code[$i]); } } //写入噪声点 private function writePoints($img,$pointNum){ for($i=0;$i<$pointNum;$i++){ $pointcolor=imagecolorallocate($img,mt_rand(0,250),mt_rand(0,250),mt_rand(0,250)); imagesetpixel($img,mt_rand(1,$this->width-1),mt_rand(1,$this->height-1),$pointcolor); } } //写入噪声线条 private function writeLines($img,$lineNum){ for($i=0;$i<$lineNum;$i++){ $linecolor=imagecolorallocate($img,mt_rand(0,250),mt_rand(0,250),mt_rand(0,250)); $linex=mt_rand(1,$this->width-1); $liney=mt_rand(1,$this->height-1); imageline($img,$linex,$liney,$linex+mt_rand(0,4)-2,$liney+mt_rand(0,4)-2,$linecolor); } } } $verifyCode = new captcha('/volume1/web/captcha/simsunb.ttf'); $verifyCode->setSize(100,50,6); $verifyCode->genImg(100,100); $verifyCode->outputImg(); $_SESSION['verifyCode']=md5($verifyCode->getCode()); echo $_SESSION['verifyCode']; ?>
调用页面(js验证部分就不写在这里了,方法就是拿SESSION的verifyCode和用户的字符进行md5加密校对就行了,这里需要提一下的就是,有些人会只在前端做校验,这其实只能算作预校验,所谓防君子不防小人,必须要在后端提交的时候做二次校验,否则暴力提交可以轻松绕过JS的预校验):
<img src="/captcha/index.php" />
哈哈,还不错吧,有兴趣的可以再把图像做的复杂,但是会被群众扔番茄的哟!
当然,很多框架其实是提供了验证码辅助函数的,不需要自己去写相关的代码,比如说CI框架:
$this->load->helper('captcha'); $vals = array( 'word' => 'Random word', 'img_path' => './captcha/', 'img_url' => 'http://example.com/captcha/', 'font_path' => './path/to/fonts/texb.ttf', 'img_width' => '150', 'img_height' => 30, 'expiration' => 7200 ); $cap = create_captcha($vals); echo $cap['image'];
是不是很简单!