对,就是那个由奇怪字符组成的验证码

8/31/2015来源:PHP技巧人气:1112

对,就是那个由奇怪字符组成的验证码

什么是验证码?

  验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写。大名鼎鼎的图灵测试,通过一个人能答得上来,而机器不能正确回答的问题(五花八门形式的问题都有。。。),用以自动区分计算机和人。偶尔有不是通过图片,而是短信验证码来验证。

  验证码的缺点也是显而易见的,破坏了用户体验(盯着看了好几遍还是傻傻分不清楚或者验证码图片显示不出来),所以需要不断在用户体验和抵挡机器OCR(光学字符识别)之间寻找平衡点。

  * 像手机号注册,需要填写身份证号之类的手段,也都附带有类似的限制机器自动提交的目的。

为什么要用验证码?主要防范哪些方面?

  验证码作为一种抵挡机器人手段,用来阻止各种批量暴力提交行为。

  主要防范:

  •   恶意注册大量用户账号
  •   使用枚举法进行密码破解
  •   机器人发帖
  •   意识不清醒的时候误操作,比如说酒后。。。

创新用法

  除了用来反机器人程序,谷歌公司还将大数据概念发扬光大,在09年收购了验证码公司reCAPTCHA,将古籍里面不确定的字和确定的字合成成验证码图片。当大量用户识别确认确定的那个字之后,通过统计排序就能得到不确定的那个字最有可能的对应文字,从而“免费”利用了填写验证码的资源。自后又将这个技术发扬光大,把谷歌街景里不能识别的商铺和路标也放在验证码里识别。

防止OCR的方法

  当天还是蓝的时候,CAPTCHA 也就是一个干干净净的字符串图片。后来就:

  • 添加背景复杂度 - 各种噪声,什么线条啊,黑点啊,彩色斑斓,边缘放置一些干扰字符,但是不作为正式验证码。
  • 增加前景复杂度 - 让字都黏在一起,让识别程序不能切割文字,字体颜色也尽量靠近背景色,文字变形,压缩,倾斜,各种扭曲
  • 使用自己设计的手写字体
  • 算术题,微积分,生活常识,历史地理知识!!!(简直就是反学渣系统)
  • 使用动画效果,让程序分不清哪些帧是有用的
  • 特别的模式,比如说在10张人像中,跳出3张老外的图片,OMG
  • 干脆只提供音频验证码给用户
  • 是不是应该再开发一个需要简单玩游戏的验证码机制呢?哈哈

辅助视听

  当验证码太难看清(字符分割不好,变形太厉害,显示屏太烂等),用户就需要使用耳朵听的方法得到验证码。一般来说,可以在服务器端根据生成的字符串,动态合成一段MP3,传送到客户端进行播放。

简单的编程实现方法介绍

<?phpif(!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'];

是不是很简单!