唐乾林,黎现云
(重庆电子工程职业学院,重庆 401331)
主流验证码通过提供静态的图片,比较容易被OCR软件识别;一些网站提供GIF动态的验证码图片,使识别器不容易辨识哪一个图层是真正的验证码图片。这种方法不仅可以提供清晰的图片,还可以更有效地防止识别器的识别。目前,比较常见的GIF动态验证码都是由英文字母或数字组成的,这实现起来相对简单但安全性也相对较弱。为了提高验证码的安全性,就有了中文GIF动态验证码的提法[1]。为了网站和验证码的安全,为了中文GIF动态验证码能得到广泛的应用,本文以代码复用和易于维护为出发点,进行了中文GIF动态验证码生成器的探索与研究。
要产生中文GIF动态验证码就必须能做到随机生成汉字,要想能生成汉字就必须熟悉汉字的编码及其产生方法。
生成随机数的方法很多,不仅可以自己编程实现,也可以复用现成的代码。为了方便,本研究使用PHP中的mt_rand或rand函数完成随机数的生成。rand函数默认使用libc随机数发生器,很多老的libc的随机数发生器具有一些不确定和未知的特性而且效率很低;mt_rand则是用了Mersenne Twister中已知的特性作为随机数发生器,其产生随机数值的平均速度比libc提供的rand快4倍。所以在PHP中mt_rand函数是非正式用来替换rand的[2]。
语法:
rand(min,max)
mt_rand(min,max)
min,max皆为可选,规定随机数产生的范围。
如果没有提供可选参数min和max,则返回0~RAND_MAX之间的伪随机整数。例如,想要10~1 000(包括10和1000)之间的随机数,用rand(10,1 000)或mt_rand(10,1 000)。
1.2.1 GB2312
GB2312标准共收录6 763个汉字,其中一级汉字3 755个,其编码范围为0xB0A1~0xD7F9;二级汉字3 008个,其编码范围为0xD8A1~0xF7FE;同时,GB2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符[3]。
GB2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。但对于人名、古汉语等方面出现的罕用字GB2312不能处理,这导致了后来GBK及GB18030汉字字符集的出现。
1.2.2 GBK
GBK编码是在GB2312标准基础上的内码扩展规范,共收录了21 003个汉字,完全兼容GB2312标准,支持国际标准ISO/IEC10646—1和国家标准GB13000—1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。
GBK是采用单双字节变长编码,英文使用单字节编码,完全兼容ASCII字符编码,中文部分采用双字节编码,其编码范围为8140~FEFE,首字节在81~FE,尾字节在40~FE,剔除 xx7F一条线。总计23 940个码位,共收入21 886个汉字和图形符号,其中汉字(包括部首和构件)21 003个,图形符号883个。
1.2.3 GB18030
GB18030全称为信息技术中文编码字符集,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB2312完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB18030共收录汉字70 244个。
GB18030采用单字节、双字节和四字节3种方式对字符编码。(1)单字节部分采用GB/T 11383的编码结构与规则,使用0x00~0x7F码位(对应于ASCII码的相应码位)。(2)双字节部分,首字节码位从0x81至0xFE,尾字节码位分别是0x40~0x7E和0x80~0xFE。(3)四字节部分采用GB/T 11383未采用的0x30~0x39作为对双字节编码扩充的后缀,这样扩充的四字节编码,其范围为0x81308130~0xFE39FE39。其中,第一、第三个字节编码码位均为0x81~0xFE,第二、第四个字节编码码位均为0x30~0x39。
1.2.4 Unicode
Unicode、统一码也叫万国码、单一码是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式发布1.0版本,2005年3月31日发布4.1.0版本,2021年9月14日发布14.0版本。常用汉字编码范围为4E00~9FFF,计有20 992个。扩展汉字编码范围为20000~2FA1F,计有53 424个。所以,Unicode所能表示的汉字有74 416个。
1.2.5 UTF-8
UTF-8是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
Unicode只是一组字符设定或者说是从数字和字符之间的逻辑映射的概念编码,但它并没有指定代码点如何在计算机上存储。在Unicode官方资料中,Unicode的编码方式有三种:UTF-8,UTF-16,UTF-32。由于UTF-8与字节序无关即无需BOM,同时兼容ASCII编码,使得UTF-8编码成为现今互联网信息编码标准而被广泛使用。
根据汉字编码规则及前面的分析,随机生成的汉字,其编码可为GB2312的一级汉字或二级汉字,也可以直接采用Unicode编码。
生成GB2312汉字的PHP代码如下:
function getChar($num){ // $num为生成汉字的数量
$b = '';
for ($i=0;$i<$num;$i++){
$b.=iconv('GB2312','UTF-8',chr(mt_rand(0xB0,0xD7)).chr(mt_rand(0xA1,0xF9)));
}
return $b;
}
根据前面的分析与设计及PHP语言,实现的中文GIF动态验证码生成器的主要代码如下:
//GIF动画类
class GIFEncoder {
var $GIF = "GIF89a";
var $VER = "GIFEncoder V2.06";
var $BUF = Array ();
var $LOP = 0;
var $DIS = 2;
var $COL = -1;
var $IMG = -1;
var $ERR = Array (
'ERR00' =>"Does not supported function for only one image!",
'ERR01' =>"Source is not a GIF image!",
'ERR02' =>"Unintelligible flag ",
'ERR03' =>"Could not make animation from animated GIF source",
);
…
}
//生成GIF图片验证
function ImageCode($string='',$width=75,$height=25){
if(empty($string)){
$len = mt_rand(4,6);
$width=16*$len;
$string=getChar($len);
}
$authstr = $string;
$board_width = $width;
$board_height = $height;
// 生成一个32帧的GIF动画
for($i = 0;$i < 32;$i++){
ob_start();
$image = imagecreate($board_width,$board_height);
imagecolorallocate($image,0,0,0);
// 设定文字颜色数组
$colorList[] = imagecolorallocate($image,15,73,210);
$colorList[] = imagecolorallocate($image,0,64,0);
$colorList[] = imagecolorallocate($image,0,0,64);
…
$fontcolor = imagecolorallocate($image,0,0,0);
$gray = imagecolorallocate($image,245,245,245);
$color = imagecolorallocate($image,255,255,255);
$color2 = imagecolorallocate($image,255,0,0);
imagefill($image,0,0,$gray);
$space = 15;// 字符间距
$font=array('C:WindowsFontsmsyhbd.ttc',/*微软雅黑*/
'C:WindowsFontsSTXINGKA.TTF',//华文行楷
'C:WindowsFontssimsun.ttc'/*宋体&新宋体*/);
if($i > 0){ // 屏蔽第一帧
$top=0;//缺失此项,致命错误
for ($k = 0;$k < strlen($authstr);$k++){
$colorRandom = mt_rand(0,sizeof($colorList)-1);
$float_top = rand(0,4);
$float = rand(0,3);
$size=mt_rand(13,15);//字体大小
$angle=mt_rand(0,30);//角度制表示的角度
imagettftext($image,$size,$angle,$space*$k,$top+$float_top+15,$colorList[$colorRandom],$font[mt_rand(0,count($font)-1)],mb_substr($authstr,$k,1));//支持中文
}
}
for ($k = 0;$k < 20;$k++){
$colorRandom = mt_rand(0,sizeof($colorList)-1);
imagesetpixel($image,rand()%70,rand()%15,$colorList[$colorRandom]);
}
// 添加干扰线
…
$gif = new GIFEncoder($imagedata);
header('Content-type:image/gif');
echo $gif->GetAnimation();
}
前面使用PHP语言实现了中文验证码生成器,那么这个生成器能生成中文验证码,可以编写如下的简单代码进行测试。
//调用示例
if(isset($_GET["getcode"])&& $_GET["getcode"]==1){
session_start();
$len = mt_rand(4,6);//生成验证码位数:4-6位的变长验证码
$w=16*$len;//计算显示验证码的宽度
$_SESSION['timestamp'.session_id()] = time();//设置有效时间
$randCode=getChar($len);//获得验证码
$_SESSION['code'.session_id()]=$randCode;//记录验证码
ImageCode($randCode,$w);//显示GIF动画
exit();
}
//验证示例
if(isset($_POST['gettxt']))
{
session_start();
exit($_SESSION['code'.session_id()]);
}
//HTML页面调用示例
?>
function gettxt(){
$.ajax({
type:"POST",
url:"=$_SERVER['PHP_SELF']?>",
dataType:"html",
data:{
gettxt:1
},
success:function(data){
$("#code").html(data);
},
complete:function(XMLHttpRequest,textStatus){
},
error:function(){
//请求出错处理
}
});
}
中文GIF动态验证码
经过反复运行上述代码,都可以在终端看到随机生成的有汉字动态显示的GIF动画,也可以获得当前的中文验证码(见图1),说明上述生成器没有问题,可以投入实际项目使用。
实践证明,本文的中文GIF动态验证码生成器是可行的、可用的。可以根据需要,选用一级汉字或者二级汉字,在简单的输入验证中,也是比较安全高效的。若选用Unicode编码,则有可能出现生僻字,从而使输入的难度增加,但可以改造成点击验证,从而可以进一步完善此中文验证码生成器。