手邊有個服務需要圖片驗證(captcha)的功能,所以翻了一下之前寫的code,順便整理一下跟大家分享。這邊的code是產生出來的圖片是最簡單的樣式,僅是四個亂數產生的字母(有字母可能重複),外加上一些黑點雜訊來增加辨識上難度,為了避免黑點干擾造字母的辨識困難,我剔除掉一些長相相近的字母,希望增加使用者輸入正確的機率,畢竟圖片驗證是要拿來擋機器人而不是拿來刁難真正的人類阿。
進入正題,captcha在實作概念很簡單,由一隻程式動態產生圖片檔案,圖片中包含一組長度不一、有無意義均可的字串,並且將產生出來的字串值存入session中,待使用者輸入完後,在將輸入的字串與存在session中的字串相比,若是相符則通過,反之則替除。
這邊我們會使用到PHP中的GD Library,你可以透過phpinfo()來檢查你是否成功安裝該Library。此外,為了讓產生出來的字母有更多的面貌(所以你可以挑刁鑽一點的字型),請先自備好英文的truetype字型檔,將該字型檔案更名為font.ttf,放在與產生圖片程式的相同目錄下即可。別擔心,網路上一堆免費的英文字型檔可以下載使用,這邊推薦兩個:
完整程式碼如下(有加上註解了,應該非常好懂):
<?php
session_start();
Header("Content-type: image/PNG");
//在session中自訂使用authstr這個變數來儲存產生的字串
$_SESSION['authstr']="";
//產生一個90*30的圖檔
$im = imagecreate(90,30) or die("Cant's initialize new GD image stream!");
//定義使用的顏色,紅色為字體顏色,白色為底色,灰色為黑點雜訊
//若要更動,請自行更改後面三個整數,依序為R、G、B範圍皆在0~255間
$red = ImageColorAllocate($im, 255, 0, 0);
$white = ImageColorAllocate($im, 255, 255, 255);
$gray = ImageColorAllocate($im, 100, 100, 100);
//將圖片底色填滿白色
imagefill($im, 0, 0, $white);
//定義候選的字母,我剔除掉一些會混淆的,如大寫I與L
$ychar="A,B,C,E,F,H,K,L,M,N,P,R,T,U,V,W,X,Y,Z";
$list=explode(",",$ychar);
$cnt = count($list)-1;
//亂數挑選四個字母,字母可重複
for($i=0;$i<4;$i++){
$randnum=rand(0,$cnt);
$authnum.=$list[$randnum]." ";
}
//將最後挑選出來的結果存入session
$_SESSION['authstr']=str_replace(" ","",$authnum);
//將挑選出來的字串印在圖片上,這邊你必須自備truetype的英文字型檔
putenv('GDFONTPATH=' . realpath('.'));
imagettftext($im, 10, 3, 8, 24, $red, "/full/path/font.ttf", $authnum);
//加入100個黑點雜訊
for($i=0;$i<100;$i++)
imagesetpixel($im, rand()%90 , rand()%30 , $gray);
//最後將圖片產生並且印出來
ImagePNG($im);
ImageDestroy($im);
?>
這支程式最後產生出來的成果如右,請點選這個連結。(記得按F5看看字母是否每次都不同)
接著,在網頁前端的部分,新增下列HTML碼(假設上面的產生程式碼檔名為captcha.php)
<img src="captcha.php" border="0" alt="" width="90" height="30" /> 請輸入上面的驗證碼(皆為大寫英文字母):<input id="reply_form_CODE" name="reply_form_CODE" type="text" />
上面的步驟我們完成了產生圖片的前後端部分,最後便是驗證的部分,將下面這段php的程式碼插入你所需驗證的程式碼最上端即可完成驗證的手續:
$code = strtoupper($_POST['reply_form_CODE']);
if($code == "" || $code != $_SESSION['authstr'] || strlen($code) != 4){
//開始寫輸入錯誤的處理,這邊舉例是導到yahoo!kimo
header("Location: http://tw.yahoo.com/");
}
這樣就完成了實作一個簡單的圖片驗證,這種作法可以阻擋大多數的SPAM BOT的攻擊,但是如果你的網站樹敵頗多或室"內容"豐富,那可能會吸引有實作OCR的BOT攻擊,請根據自己的需求修改產生更複雜的圖型模式。

很實用! 感謝你的分享!
終於試成功了,不過一開始時候一直會收到「無法顯示錯誤的圖片」的錯誤訊息。
我把 header("Content-type: image/png"); 這行mark後,看到下面的錯誤訊息
Notice: Undefined variable: authnum xxxxxxxxxxxxxx
Warning: imagettfbbox() [function.imagettfbbox]: Problem loading glyph xxxxxxxxxxxxx
第一個只是warning,在前面加上 $authnum = ""; 就好了。
主要是第二個 warning: Problem loading glyph 比較奇怪,上網查了一下也查不出個所以然…
本來以為是字型檔的問題,換了好幾個也都沒用
後來把Apache整個stop再start…就好了……….@_@;;;
我也不知道為什麼… ~_~
Any way~~ 現在可以成功work了! 感謝你的分享!!
想請問一下,若要在驗證旁做個換一張圖片的按鈕,
不想用重新整理整個頁面,只要圖片重新整理的方法
不知道大大有研究嗎@@?
後來用iframe解決了XD
so?