2012年1月9日 星期一

利用 coldfusion 實作 CAPTCHA 驗證功能

 我當然知道這樣輸入很沒效率,但是這是目前比較好的防廣告文手段。

CAPTCHA,中文常被稱為『驗證碼』,其全名為 Completely Automated Public Turing test to tell Computers and Humans Apart (全自動區分計算機和人類的圖靈測試)。媽呀,好長的名字!!!

需要詳細瞭解者可以看中文版維基的解釋或是英文版本的。

如果需要輕鬆一點的可以看看 直角兄-淺談各式各樣的網路圖形驗證碼(Captcha)

因為網路上我真的找不太到中文關於使用原生 CFML 來設計 CAPTCHA 驗證碼的文章,

真的有點感嘆,所以就自己動手寫起來以備日後的紀錄與查找。

原始碼出處:http://www.bennadel.com/blog/873-Using-CAPTCHA-In-ColdFusion-8.htm

當然英文看不輪轉的就看我這裡的吧,我已經把註解都中文化,並且稍微修改了一些。




以下程式碼需要 CF8 以上的版本, Railo3.3 測試正常執行。
<cfsilent>
<!--- 設定表單參數 --->
<cfparam name="FORM.captcha" type="string" default="" />
<cfparam name="FORM.captcha_check" type="string" default="" />

<cftry>
 <cfparam name="FORM.submitted" type="numeric" default="0" />
<cfcatch>

<cfset FORM.submitted = 0 />
</cfcatch>
</cftry>

<!--- 設定檢查結果標記 --->
<cfset blnIsBot = true />

<!--- 檢查表單是否 submit --->
<cfif FORM.submitted>
 <!--- Decrypt 解密指令最好放在 CFTry / CFCatch 區塊內
    避免有意駭客手段導致程式碼曝光,由其畫面發生exception時 --->

 <cftry>
 <!--- 解密表單內容 --->
 <cfset strCaptcha = Decrypt(FORM.captcha_check,"bots-aint-sexy","CFMX_COMPAT","HEX") />
 <!--- 檢查解密後比對使用者輸入的文字是否相同 --->
 <cfif (strCaptcha EQ FORM.captcha)>
  <!--- 相同的話設定標記為否 --->
  <cfset blnIsBot = false />
 </cfif>
 <!--- 意外錯誤處理 --->
 <cfcatch>
  <!--- 確定標記被設定 --->
  <cfset blnIsBot = true />
 </cfcatch>
 </cftry>
</cfif>

<!--- 頁面整理前先用java產生一組亂數 CAPTCHA 英數文字,然後交給後續處理...
      建立一組有效的英文/數字陣列,但不使用0和1以免和英文字的O與I混淆 --->

<cfset arrValidChars = ListToArray(
"A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z," &
"2,3,4,5,6,7,8,9") />

<!--- 利用java擾亂陣列 --->
<cfset CreateObject("java","java.util.Collections").Shuffle(arrValidChars) />

<!--- 然後取得陣列前5碼當作是CAPTCHA文字 --->
<cfset strCaptcha = (
arrValidChars[ 1 ] &
arrValidChars[ 2 ] &
arrValidChars[ 3 ] &
arrValidChars[ 4 ] &
arrValidChars[ 5 ] ) />

<!--- 此時就可以把CAPTCHA文字當成是產生圖片的內容,因此需要在表單上放置一組可以
  參考的資料,以便submit後可以比對,但是我們不想將CAPTCHA文字直接擺入表單,如此就
  會被拿來破解,所以我們將產生的CAPTCHA文字進行加密 --->

<cfset FORM.captcha_check = Encrypt(strCaptcha,"bots-aint-sexy","CFMX_COMPAT","HEX") />
</cfsilent>

<cfoutput>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>ColdFusion CFImage / CAPTCHA Demo</title>
</head>
<body>
 <h1>ColdFusion CFImage / CAPTCHA Demo</h1>

 <form action="#CGI.script_name#" method="post">
  <!--- 用來識別是否送出表單的欄位 --->
  <input type="hidden" name="submitted" value="1" />
  <!--- 將加密過的參考資料以不顯示方式置於表單中 --->
  <input type="hidden" name="captcha_check" value="#FORM.captcha_check#" />
  <p>
  <!--- 輸出CAPTCHA圖片,可以由difficulty來設定混亂程度 --->
  <cfimage
  action="captcha"
  height="45" width="200"
  text="#strCaptcha#" difficulty="medium"
  fonts="verdana,arial,times new roman,courier" fontsize="28" />
  </p>
  <label for="captcha">Please enter text in image:</label>
  <input type="text" name="captcha" id="captcha" value="" />
  <input type="submit" value="Submit" />
  <br />
  <!--- 底下用來呈現檢查的結果 --->
  <cfif FORM.submitted>
   <h3>Bot Validation Results</h3>
   <!--- 檢查是否為bot(機器人) --->
   <cfif blnIsBot>
    <p>Error input!</p>
   <cfelse>
    <p>You are right!</p>
   </cfif>
  </cfif>
 </form>
</body>
</html>
</cfoutput>



其實 ColdFusion 8 以上的版本就已經支援 cfimage 的 captcha 圖片輸出,

剩下的就是如何產生圖片內容以及 驗證字串 的加/解密了。


A、頁面一開始執行時,並沒有被 Submit ,所以會由 ListToArray 產生單字陣列,

然後利用 CreateObject("java","java.util.Collections").Shuffle 擾亂陣列,

再選出前五碼作為驗證字串,這個長度可以自行決定,一般而言4~8碼都可以。

最後使用 Cfimage 的 Captcha 產生方式,difficulty可以決定是否容易識別,這個指令要注意的是

高度、寬度、字型大小與驗證字串的字元多寡,不適當的設定會讓使用者不易辨識,

或是佔用過大的畫面(版面)。

除了圖形產生以外,很重要的的是驗證字串的加密產生,這裏使用的是Encrypt指令來編碼,

使用的私鑰是 "bots-aint-sexy" 字串,將產生的加密字串captcha_check同時以隱藏方式置放在

表單裡面,加密字串的用途在於不被使用者端程式(BOT)直接識別出驗證圖片所屬的內容,

也能夠讓使用者端傳回輸入字串後進行比對是否正確。


B、當使用者輸入驗證圖片所應該表示的內容字串,並 Submit 送出表單,就會由頁面最上方

的程式碼來檢查,送回來的資訊有兩個重要的變數captcha_check和captcha,將captcha_check

Decrypt以相同的私鑰解密以後再與captcha比對是否相同,即可得知正確與否,

此處要注意的兩點是:

一、Decrypt 必須用<cftry></cftry>包覆起來,以防止駭客利用字串編解碼漏洞進行攻擊,導致

Decrypt指令發生exception,而露出程式碼。

二、<cfif>的字串比較是沒有區分大小寫的。


另外,直角兄有提到利用中文來當驗證字串,IDEA挺好的,所以我也來改寫一下:

我把 ListToArray 改成:
<cfset arrValidChars = ListToArray(
"一,二,三,四,五,六,七,八,九,零,國,兵,近,民,黨,親,愛,精,誠,你,我,他,不,謝,區,蔡,英,文,馬,九,宋,楚,魚") />


再把 Cfimage 改成:

  <cfimage
  action="captcha"
  height="45" width="200"
  text="#strCaptcha#" difficulty="medium"
  fonts="細明體,新細明體,標楷體" fontsize="28" />


耶!成功.....


改成中文還是有需要特別注意的地方,那就是cfm檔案儲存必須使用 big5 編碼方式,

Cfimage 裡面的 fonts 必需都使用 server 上擁有的中文字型檔


0 個回應: