PHP实现算式与汉字验证码:原理、代码示例及注意事项
传统字母数字验证码易被OCR技术破解,本文详述如何利用PHP GD库开发抗识别能力更强的算式与汉字验证码,涵盖图像绘制、随机干扰添加及Session验证流程,帮助开发者提升表单安全性。
验证码的安全性与技术选型
在Web表单开发中,验证码是防止恶意提交的重要手段。简单的纯数字或字母验证码由于字符特征明显,极易被光学字符识别(OCR)工具自动破解。相比之下,算式验证码(如 $1+3=$)和汉字验证码利用了更复杂的语义和视觉特征,显著增加了自动化脚本的识别难度,是提升系统安全性的有效方案。
实现算式验证码
第1步:定义计算逻辑与会话存储
首先需要初始化会话并生成随机算式。代码中通过 mt_rand() 生成两个随机数和运算符号,利用 eval() 计算结果并保存至 $_SESSION['authnum_session'] 中,以便后端验证用户输入。
第2步:创建画布与绘制底色
使用 imagecreate(68, 28) 创建固定尺寸的PNG图像,并使用 imagecolorallocate() 定义白色背景。这一步决定了验证码的最终外观尺寸。
第3步:渲染算式文本
使用 imagestring($im, 5, 10, 8, $code, $black) 将生成的算式字符串绘制到画布指定位置。这里采用内置字体,简单高效。
第4步:添加噪点以增强混淆
为防止机器识别,通过循环调用 imagesetpixel() 在画布上随机撒下黑色、红色和灰色的噪点像素,增加OCR识别的难度。
第5步:输出图像
最后通过 imagepng() 输出PNG格式图像流,并使用 imagedestroy() 释放内存资源。
<?php
session_start();
header("Content-type: image/png");
$num1 = mt_rand(0,9);
$num2 = mt_rand(1,9);
$type_str = "+-*";
$type = substr($type_str,rand(0,2),1);
$change = mt_rand(1,3);
// 根据随机类型决定显示格式及存储值
if($change==1){
$code = "$num1$type$num2=?";
$result = "$verifyCode=$num1$type$num2;";
eval($result);
$_SESSION['authnum_session'] = $verifyCode;
}elseif($change==2){
$result = "$verifyCode=$num1$type$num2;";
eval($result);
$code = $num1.$type."_="$verifyCode;
$_SESSION['authnum_session'] = $num2;
}else{
$result = "$verifyCode=$num1$type$num2;";
eval($result);
$code = "_".$type.$num2."="$verifyCode;
$_SESSION['authnum_session'] = $num1;
}
$im = imagecreate(68,28);
$black = imagecolorallocate($im, 0,0,0);
$white = imagecolorallocate($im, 255,255,255);
$gray = imagecolorallocate($im, 200,200,200);
$red = imagecolorallocate($im, 255, 0, 0);
imagefill($im,0,0,$white);
imagestring($im, 5, 10, 8, $code, $black);
for($i=0;$i<70;$i++) {
imagesetpixel($im, mt_rand(0, 58) , mt_rand(0, 28) , $black);
imagesetpixel($im, mt_rand(0, 58) , mt_rand(0, 28) , $red);
imagesetpixel($im, mt_rand(0, 58) , mt_rand(0, 28) , $gray);
}
imagepng($im);
imagedestroy($im);
?>
实现汉字验证码
第1步:准备字库与随机抽取
汉字验证码需要较大的字库字符串。代码中定义了一个包含常用汉字的长字符串 $ch_str,并使用 mb_strlen 和 mb_substr 基于UTF-8编码随机抽取4个汉字,存入 $_SESSION。
第2步:加载自定义字体
默认PHP内置字体不支持中文,因此必须指定TTF字体文件。代码中设定 $fnt = "simhei.ttf"(黑体),开发者需确保该字体文件存在于服务器当前目录或绝对路径中,否则无法正确渲染。
第3步:绘制背景干扰元素
除了常见的噪点,本例还添加了椭圆弧(imagearc)和直线(imageline)作为干扰项,并通过大量随机彩色线条(noise_num 控制数量)进一步混淆视觉焦点。
第4步:使用GD库绘制文字
这是核心步骤。使用 ImageTTFText() 函数,指定字体、字号(rand(18,20))、旋转角度(rand(0,20))以及坐标,将汉字绘制到图像上。随机角度和大小使得字形更具辨识度且难以被模板匹配。
第5步:输出与清理
同样以 ImagePNG 输出并销毁资源。
<?php
session_start();
$ch_str="的一是在了不和有大这主中人上为们地个用工时要动国产以我到他会作来分生对于学下级就年阶义发成部民可出能方进同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批如应形想制心样干都向变关点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫康遵牧遭幅园腔订香肉弟屋敏恢忘衣孙龄岭骗休借丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩";
$len = mb_strlen($ch_str,"utf-8");
$str = array();
for($i=0;$i<4;$i++){
$pos = mt_rand(0,$len-5);
$str[] = mb_substr($ch_str,$pos,1,"utf-8");
}
$authnum_session = implode("",$str);
$_SESSION['authnum_session'] = $authnum_session;
Header("Content-type: image/PNG");
$image_x=100;
$image_y=50;
$im = imagecreate($image_x,$image_y);
$bkg = ImageColorAllocate($im,255,255,255);
$fnt = "simhei.ttf";
$white=ImageColorAllocate($im,234,185,95);
imagearc($im, 150, 8, 20, 20, 75, 170, $white);
imagearc($im, 180, 7,50, 30, 75, 175, $white);
imageline($im,20,20,180,30,$white);
imageline($im,20,18,170,50,$white);
imageline($im,25,50,80,50,$white);
$noise_num=3000;
$line_num=50;
$rectangle_color=imagecolorallocate($im,0xAA,0xAA,0xAA);
$noise_color=imagecolorallocate($im,0x00,0x00,0x00);
$font_color=imagecolorallocate($im,0x00,0x00,0x00);
for($i=0;$i<$noise_num;$i++)
{
imagesetpixel($im,mt_rand(0,$image_x),mt_rand(0,$image_y),$noise_color);
}
for($i=0;$i<$line_num;$i++)
{
$line_color=imagecolorallocate($im,mt_rand(0,255),mt_rand(0,255),mt_rand(0,255));
imageline($im,mt_rand(0,$image_x),mt_rand(0,$image_y),mt_rand(0,$image_x),mt_rand(0,$image_y),$line_color);
}
for ($i=0;$i<4;$i++)
{
ImageTTFText($im, rand(18,20), rand(0,20), rand(($image_x/4)*$i+$image_x/100,($image_x/4)*$i+$image_x/8), rand($image_y/2+$image_y/10,$image_y/2+$image_y/5), $font_color, $fnt, $str[$i]);
}
ImagePNG($im);
ImageDestroy($im);
?>
关键注意事项与排错
汉字验证码的Session一致性问题
在使用汉字验证码时,开发者可能会遇到一个常见陷阱:
- 直接访问脚本:如果直接在浏览器地址栏输入脚本路径,刷新页面获取
$_SESSION,发现内容与显示的汉字不一致。 - 通过Img标签引用:如果在HTML中使用
<img src="captcha.php">引用该脚本,再次获取$_SESSION,两者内容则完全一致。
原因分析:这通常与客户端Cookie的传递有关。当通过 <img> 标签请求图片时,浏览器会自动携带当前域名的Session Cookie,导致服务端正确关联Session ID。而在地址栏直接访问时,如果之前没有正确的Cookie上下文,可能导致新的Session启动,从而产生错位。建议在正式环境中务必通过表单提交的图片方式展示验证码。
字体文件配置
汉字验证码依赖外部TTF字体文件(如代码中的 simhei.ttf)。如果服务器上不存在该字体,或者路径错误,输出的图像中将出现方框或空白,而非汉字。请确保字体文件存在且具有可读权限。
以上就是PHP实现算式与汉字验证码的详细内容,更多关于PHP图形处理与安全验证的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









