簡單實作PHP圖片驗證

手邊有個服務需要圖片驗證(captcha)的功能,所以翻了一下之前寫的code,順便整理一下跟大家分享。這邊的code是產生出來的圖片是最簡單的樣式,僅是四個亂數產生的字母(有字母可能重複),外加上一些黑點雜訊來增加辨識上難度,為了避免黑點干擾造字母的辨識困難,我剔除掉一些長相相近的字母,希望增加使用者輸入正確的機率,畢竟圖片驗證是要拿來擋機器人而不是拿來刁難真正的人類阿。

進入正題,captcha在實作概念很簡單,由一隻程式動態產生圖片檔案,圖片中包含一組長度不一、有無意義均可的字串,並且將產生出來的字串值存入session中,待使用者輸入完後,在將輸入的字串與存在session中的字串相比,若是相符則通過,反之則替除。

這邊我們會使用到PHP中的GD Library,你可以透過phpinfo()來檢查你是否成功安裝該Library。此外,為了讓產生出來的字母有更多的面貌(所以你可以挑刁鑽一點的字型),請先自備好英文的truetype字型檔,將該字型檔案更名為font.ttf,放在與產生圖片程式的相同目錄下即可。別擔心,網路上一堆免費的英文字型檔可以下載使用,這邊推薦兩個:

  1. fontspace
  2. Fonts 500

完整程式碼如下(有加上註解了,應該非常好懂):

<?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攻擊,請根據自己的需求修改產生更複雜的圖型模式。

本篇發表於 PHP, 技術 並標籤為 , 。將永久鏈結加入書籤。

簡單實作PHP圖片驗證 有 5 則回應

  1. firestoke 說道:

    很實用! 感謝你的分享! :)

  2. firestoke 說道:

    終於試成功了,不過一開始時候一直會收到「無法顯示錯誤的圖片」的錯誤訊息。
    我把 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了! 感謝你的分享!! :)

  3. chen 說道:

    想請問一下,若要在驗證旁做個換一張圖片的按鈕,
    不想用重新整理整個頁面,只要圖片重新整理的方法
    不知道大大有研究嗎@@?

  4. chen 說道:

    後來用iframe解決了XD

發表迴響

您的電子郵件位址並不會被公開。 必要欄位標記為 *

*

您可以使用這些 HTML 標籤與屬性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>