廣告廣告
  加入我的最愛 設為首頁 風格修改
首頁 首尾
 手機版   訂閱   地圖  簡體 
您是第 11186 個閱讀者
 
發表文章 發表投票 回覆文章
  可列印版   加為IE收藏   收藏主題   上一主題 | 下一主題   
tailin! 手機
個人頭像
個人文章 個人相簿 個人日記 個人地圖
小有名氣
級別: 小有名氣 該用戶目前不上站
推文 x0 鮮花 x13
分享: 轉寄此文章 Facebook Plurk Twitter 複製連結到剪貼簿 轉換為繁體 轉換為簡體 載入圖片
推文 x0
文章表情[SQL][教學] 淺談許蓋功
許蓋功何許人也?
許蓋功這號人物,只要曾經用過php+mysql架站的人,無人不知無人不曉,且絕對是這些架站人心中永遠的痛....
(摘自osCommerce購物網站架設實戰)




仔細探究原因,你會發現,錯的人其實不該是許蓋功,而是通稱大五碼的BIG5碼,關於大五碼的歷史傳說眾說紛紜,無一定論。我們並不評論當初編碼的對錯與是非,對這歷史有興趣的讀者不妨到google搜尋"BIG5",相信可以找到一堆資料,或是到圖書館翻翻下列兩本書:

書名:中文字碼:萬碼奔騰,一碼當先
作者:黃大一
出版單位:永麒科技

書名:國字整理小組十年
作者:謝清俊、黃克東
出版單位:資訊應用國字整理小組

既然大五碼聽來似乎有些問題,為何目前幾乎所有的繁體中文卻又都採用此一編碼呢?在西元1983-1984年間,個人電腦正在台灣逐漸推廣,電腦上的套裝軟體也開始盛行,為了解決電腦處理中文的問題,因而制定一套中文內碼,也就是我們通稱的"BIG5碼",又稱大五碼。而經歷過這段時間的人,一定不會忘記當初倚天中文的行銷手法,在國外軟體廠商開始引進原版軟體觀念的當時,倚天中文卻反其道而行,允許校園甚至一般使用者無條件且免費複製其中文系統,因此,讓倚天中文在當時幾乎成為中文的標準,也由於倚天中文正是採用BIG5編碼,嚴格說來,也就是BIG5碼為何一直沿用到現今的主要原因了。

大五碼錯在哪裡?
錯在編碼時沒有把美國標準資訊交換碼ASCII(American Standard Code for Information Interchange)的控制碼排除在外,凡是唸過計算機概論的人都知道ASCII是以byte為單位,又1 byte=8 bits,所以ASCII最多可以編2^8=256個字元,對於只有26個字母的英文語系國家來說已綽綽有餘,但對於有幾萬字的中文絕對不夠,因此必須用兩個byte來代表一個中文字,如"中"字的編碼即是"A4A4"。然而,BIG5碼設計時為了避免與ASCII衝突,每個中文字的第一個byte僅使用ASCII裡的高字元(129-255),但在第二個byte卻用到了部分低字元(1-128),這正是BIG5碼在日後應用上造成極大不便的最大幫兇了。

為何BIG5碼專找php+mysql麻煩?
原因有三:
一、sql隱碼問題:
我們知道,如果要在mysql資料庫中擷取得資料時的語法為:

select * from administrator where id='ABC' and passwd=񟡂'

假設我有一個login.php的網頁,內容是用來輸入id和passwd值的表單(from),若有人直接於網址列輸入:

login.php?id=ABC&passwd='%20or%201=1%20or%201='

由於%20會被瀏覽器解譯為空白,因此最後丟到mysql的sql語法是:

select * from administrator where id='ABC' and passwd='' or 1=1 or 1=''

這是一個恆為真的式子,騙過了驗證而取得administrator的權限。
因此,單引號變成了頭號隱形殺手。
二:php 的跳脫字元
5C在php裡面是被拿來當跳脫字元,也就是說當變數裡面的文字帶有 、單引號或雙引號時,為了要可以正確顯示這些特殊字元,通常需要多加一個 \,常見的例子如:
<?php
echo "<table border=\"0\">";
?>


如果沒加,立刻會出現錯誤訊息:

Parse error: parse error, unexpected T_LNUMBER, expecting ',' or '' in c:appservwwwcode.php on line 2

這樣,問題就來了,當我們要插入一筆資料到資料庫如:

INSERT INTO mytable VALUES ('許蓋功\');

由於功的第二個byte是 5C ,加上後面接的是單引號,因此經過解譯之後,最後面的單引號卻認定為文字,因而導致sql語法少了最後那個單引號,當然就寫不進資料庫而發生錯誤了。
三、addslashes與stripslashes函數:
為了解決單引號可能被用來當成攻擊資料庫的工具,一般在寫php程式時會利用addslashes函數將變數裡的單引號前加入一個跳脫字元,如上述原本在passwd輸入:
' or 1=1 or 1='
可以騙過驗證,在經過addslashes函數處理後變成:
\' or 1=1 or 1=\'
這樣便可以避免單引號被用來當成攻擊資料庫的工具了。但是,如此一來,跳脫字元會被當成輸入文字直接寫入資料庫內,因此,當我們寫入資料庫時若用了addslashes函數,從資料庫取出該筆資料時就必須使用stripslashes將刪去,否則顯示出來的資料就會多一個的跳脫字元了。

BIG5不只找php麻煩,連unix都倖免於難
7C 是 ASCII 裡的 pipe '|' 用過 unix 的應該知道它是作什麼的,舉一個簡單的例子,如果你用 ftp 上傳一個 "四.doc" 的檔名到 unix ,傳完後立刻變成 "北.doc',我想太多人有過這種經驗,原因無他,中文字 "四" 的 BIG5 碼是"A57C",當 unix 看到 7C 時會覺得莫名其妙,上傳一個 "|" 給我做什麼?於是就自己處理掉了...
因此,你可以想像只要是中文字第二個byte是 "7C" 的,保證也都難逃BIG5的魔掌。

許蓋功的解決之道
一、去除程式裡出現問題那段程式碼裡的stripslashes函數,如此,除了顯示"許蓋功\"時可能變成"許蓋功\"之外,似乎沒有太大的問題,但是,mysql server的隱碼及跳脫字元問題還是存在的。
二、使用big5_func字串處理函數集
如果你曾經仔細研究筆者在OSC裡處理許蓋功的方法,你應該就會發現[webroot]/catalog/includes/languages/tchinese 目錄下有一個叫big5_func的資料夾,其實就是網路上的高人為了解決BIG5的問題而寫的函數集,我們稱之為"big5 字串處理函數集"。
為了尊重原作者版權,特別將相關訊息摘錄於后:
/*
程式 : big5 字串處理函數集
檔名 : big5_func.inc
作者 : Pigo Chu<pigo@ms5.url.com.tw>
說明 :
這些函數是以 PHP4 來處理 big5 字元
任何人都可以自由散佈本程式
寫這些程式是看見 LinuxFab 上討論區上很多人有中文問題才寫的
我不能保證會發生什麼問題 , 若有 bug 請來信討論不要謾罵
時間 : 2002/4/21
版本 : 0.10

版本介紹 :
0.01 版(2001/5/27) 提供的函數
string big5_addslashes(string str) : 與 PHP addslashes 一樣的功能 , 可以處理中文
string big5_stripslashes(string str) : 與 stripslashes 一樣
int big5_strlen(string str) : 與 strlen 功能相同
string big5_substr(string str,int start , int length) : 與 substr 一樣
string big5_strtolower(string str) : 與 strtolower 一樣
string big5_strtoupper(string str) : 與 strtoupper 一樣

0.02 版(2001/5/28) 提供的函數
string big5_chunk_split(string $str, [int $chunklen=76] , [string $end=" "]) : 與 chunk_split 相同

0.03 版(2001/6/16) 提供的函數
string big5_strpos(string haystack ,string needle , int [offset]) : 傳回第一個找到 $str 的位置

0.04 版(2001/11/12) 修改 bug
把一些定義與判斷式的寫錯修正 , 感謝網友小藍 ...

0.05 版(2002/2/13) 修正 big5_stripslashes()
此函數會把所有 "" 去掉的問題 , 謝謝網友Neil指正

0.06 版(2002/2/22) 新增 big5_str_replace()
此函數用法與 str_replace() 一樣

0.07 版(2002/4/12) 新增 int big5_stroke($string)
此函數可計算單一中文字的筆劃 , 若輸入的不是中文則return false
 
0.08 版(2002/4/19) 新增 big5_unicode($string) , big5_utf8_encode(),big5_utf8_decode(), 修改 big5_stroke($string)
big5_unicode() 可以將中文轉成多國語言給網頁用的碼
big5_utf8_encode() 可以將中文轉成 UTF8 碼
big5_utf8_decode() 可以將 UTF8 轉成 BIG5 碼
big5_stroke() 改成開檔方式 , 這樣不用到此函數時比較省記憶體

0.09 嘔心版 (2002/4/20) 修正許多函數寫法 , 提昇效能
據測試 : big5 轉 utf 與 big5 轉 unicode 提昇效能 0.08 版效能 10 倍以上
測試 1 萬 個中文字轉 utf8 大約需要 2.2 秒 , 比前一版(居然超過2分鐘快上非常多)
雖然還不是挺滿意 , 不過已經可以接受
另外 big5_substr , big5_strlen 改了一些寫法所有快了一點點 ...

0.10 版 (2002/4/21) 提昇 bi5 轉 utf8 , unicode , 效能再提升加快 2 倍
據我自己的電腦測試 , 測試 1 萬中文字轉 utf8 已經可以低於 1 秒了 ...
big5_substr() 重寫也加快了一點點速度

*/
這就是目前筆者使用於OSC處理中文字串的函數集,有興趣的讀者不妨自行參考big5_func.inc一檔。事實上如果要處理許蓋功等\"5C"的問題,在big5_func裡只用到兩個函數,也就是big5_addslashes和big5_stripslashes,而這兩個函數的功能除了擁有原來addslashes跟stripslashes的功能之外,最重要的就是可以分辨出哪些是中文字,哪些才是真正的跳脫字元。舉例來說:
使用php時的程式碼:

echo addslashes('許蓋功\');
echo stripslashes('許蓋功\');
?>

結果是:

許\蓋\功\
頂?

使用big5_func字串函數集的程式碼:

echo big5_addslashes('許蓋功\');
echo big5_stripslashes('許蓋功\');
?>

結果是:

許蓋功
許蓋功

這樣的確可以解決php處理蓋功等相關中文字的問題,但是,同樣的當你寫入mysql資料庫時仍然無法解決跳脫字元的問題而出現稍早提過的錯誤,因為" 許蓋功\"內還是含有"5C"的字元。因此,當你決定使用big5_func來處理時,就必須將mysql的charset也一並改為BIG5且不可讓許蓋功等字放在要插入資料字串的最後面。請參考上述php的跳脫字元一節。此時,還有一個比較嚴重的問題是,變更charset是必須重新編譯mysql的,也就是說如果你是已經運作正常的主機,必須重新安裝mysql server並加入charset=big5的參數,若如果你是租用的網頁主機,那問題就會變得更為複雜,因為主機供應商通常不會特別因為你要使用big5_func的函數而重新編譯他的mysql server。
因此,筆者於光碟附的繁體中文OSC版本,僅針對mysql server的charset為latin1做修正,所以,如果你的mysql server的charset設定為big5,則參考本章所提的觀念,應該可以很輕鬆修正許蓋功的問題了。

筆者於目前OSC版本的修改方式為(mysql server charset=latin1)
1.開啟 [webroot]/catalog/includes/functions/database.php
找到
function tep_db_input($string) {
  return addslashes($string);
}

function tep_db_prepare_input($string) {
  if (is_string($string)) {
    return trim(tep_sanitize_string(stripslashes($string)));
改為
function tep_db_input($string) {
  return addslashes(big5_stripslashes(($string)));
}

function tep_db_prepare_input($string) {
  if (is_string($string)) {
    return trim(tep_sanitize_string(big5_stripslashes(big5_addslashes($string))));

2.開啟[webroot]/catalog/admin/includes/functions/database.php
找到
function tep_db_input($string) {
  return addslashes($string);
}

function tep_db_prepare_input($string) {
  if (is_string($string)) {
    return trim(stripslashes($string));
改成
function tep_db_input($string) {
  return addslashes($string);
}

function tep_db_prepare_input($string) {
  if (is_string($string)) {
    return trim(big5_stripslashes($string));
這樣就可以解決大部分因許蓋功造成的問題。

OSC前台無法搜尋許蓋功等產品問題
這個問題還是跳脫字元"5C"搞的鬼,當我們在前台想要搜尋跟"許蓋功\"有關的商品時,所產生的sql語法會像:

select * from tablename where products_name like '%許蓋功\%'

這樣的sql語法放到mysql裡面,因為功的第二個byte就是"5C"跳脫字元,實際卻被誤判成:

select * from tablename where products_name like '%許蓋?\%'

因此,就造成了,明明資料庫裡有許蓋功相關的商品,就是怎樣也搜尋不到相關的產品資料。
那麼,要如何才可以修正這個錯誤呢?答案就是想辦法讓你的sql語法變成這樣:

select * from tablename where products_name like '%許蓋功\\\%'

所以你會看到筆者的做法:
開啟[webroot]/catalog/advanced_search_result.php
約在256行:
找到
    default:
      $keyword = tep_db_prepare_input($search_keywords[$i]);
      $where_str .= "(pd.products_name like '%" . tep_db_input($keyword) . "%' or p.products_model like '%" . tep_db_input($keyword) . "%' or m.manufacturers_name like '%" . tep_db_input($keyword) . "%'";
      if (isset($HTTP_GET_VARS['search_in_deion']) && ($HTTP_GET_VARS['search_in_deion'] == Ƈ')) $where_str .= " or pd.products_deion like '%" . tep_db_input($keyword) . "%'";
      $where_str .= ')'
      break;
改成
default:
      $keyword = big5_addslashes(stripslashes(big5_addslashes(tep_db_prepare_input($search_keywords[$i]))));
      $keyword1 = big5_addslashes($search_keywords[$i]);     //output -> 功\\
      $keyword = str_replace( chr(92).chr(92) ,chr(92).chr(92).chr(92),$keyword1);   // 將 功\\ 換成 功\\\
      $where_str .= "(pd.products_name like '%" . $keyword . "%' or p.products_model like '%" . $keyword . "%' or m.manufacturers_name like '%" . $keyword . "%'";
      if (isset($HTTP_GET_VARS['search_in_deion']) && ($HTTP_GET_VARS['search_in_deion'] == Ƈ')) $where_str .= " or pd.products_deion like '%" . $keyword . "%'";
      $where_str .= ')'
      break;

後記
由於BIG5所造成的問題幾乎無所不在,筆者認為除非BIG5有一個完整的補救計劃,否則許蓋功將會一直困擾著所有架站人。在此,筆者也試圖透過這樣的說明,讓每一位想架站卻又遭受此一問題困擾的人自己找到解決的辦法。最後在此也特別聲明,BIG5的問題可能不只這些,也可能相當棘手,甚至超出筆者所能解決的範圍,但,如果你有任何問題,也歡迎你到 網路甘仔店 社群提出,相信我們有許多熱心的人可以一同來解決BIG5的問題。



Best Regard ... \(^o^)/ ... ﹒‧°∴°﹒☆°.
獻花 x0 回到頂端 [樓 主] From:台灣新世紀資通 | Posted:2005-02-23 10:16 |
piliman
數位造型
個人文章 個人相簿 個人日記 個人地圖
小人物
級別: 小人物 該用戶目前不上站
推文 x3 鮮花 x9
分享: 轉寄此文章 Facebook Plurk Twitter 複製連結到剪貼簿 轉換為繁體 轉換為簡體 載入圖片
Re:淺談許蓋功
聽說utf-8可以解決此問題
不知有沒有人 知道實作要注意哪些流程

或者有何副作用


http://www.io....com南庄民宿網
http://blog.io....com苗栗民宿旅遊網
獻花 x0 回到頂端 [1 樓] From:台灣中華電信 | Posted:2005-02-23 23:55 |
tailin! 手機
個人頭像
個人文章 個人相簿 個人日記 個人地圖
小有名氣
級別: 小有名氣 該用戶目前不上站
推文 x0 鮮花 x13
分享: 轉寄此文章 Facebook Plurk Twitter 複製連結到剪貼簿 轉換為繁體 轉換為簡體 載入圖片
Re:淺談許蓋功
我聽說 mysql 4.1 版之後就都可以以UTF-8來存取...
不過我也還不知道該怎麼做.....
是不是安裝時選擇UTF-8語言而不選BIG5呢?


Best Regard ... \(^o^)/ ... ﹒‧°∴°﹒☆°.
獻花 x0 回到頂端 [2 樓] From:台灣新世紀資通 | Posted:2005-02-25 22:35 |

首頁  發表文章 發表投票 回覆文章
Powered by PHPWind v1.3.6
Copyright © 2003-04 PHPWind
Processed in 0.056085 second(s),query:16 Gzip disabled
本站由 瀛睿律師事務所 擔任常年法律顧問 | 免責聲明 | 本網站已依台灣網站內容分級規定處理 | 連絡我們 | 訪客留言