當你學會了資料庫與 ASP 的整合,一定很高興,而且急著把所有的資料都放到資料庫,以便進行更好的資料管理。在下面這個範例中,我們將使用者的密碼存放在資料庫之中,以對使用者的帳號和密碼進行有效的管理,此資料庫的內容如下(password.mdb):
userid |
passwd |
---|
CS3431 | CS3431 |
林政源 | gavins |
陳江村 | jtchen |
葉佳慧 | beball |
根據此資料庫,我們就可以寫一個 ASP 網頁來進行帳號和密碼的認證:
其原始碼列出如下,以供讀者比較:
看起來一切沒問題,但是如果你想「駭」(Hack!) 這個網站,事實上只要輸入下列資料就可以了:
- 帳號:*****(亂打一通)
- 密碼:' or 'a'='a
(請趕快試試看!)這是為什麼呢?事實上這就是惡名昭彰的「資料隱碼」(SQL Injection)臭虫,簡單地說,就是將「帳號」和「密碼」填入具有單引號的特殊字串,造成伺服器端在接合這些欄位資料時,會意外地產生合格的 SQL 指令,造成密碼認證的成功。要特別注意的是,SQL Injection 的問題不限只發生在哪種特定平台或語言,只要是使用 SQL 指令存取資料庫內的資料,都有可能產生這個問題。
我們再來仔細看看上面這個範例,其中產生 SQL 指令的敘述如下:
SQL = "select * from password where userid='" + Request("user") + "' and passwd='" + Request("passwd") + "'";
看起來邏輯完全正確,例如當輸入帳號和密碼分別是「林政源」和「gavins」時,所得到的 SQL 指令是:
SQL = "select * from password where userid='林政源' and passwd='gavins'";
所以可以從資料庫中查到一筆資料,代表帳號和密碼正確。但是當我們帳號和密碼分別是「xyz」和「' or 'a'='a」時,所得到的 SQL 指令是
SQL = "select * from password where userid='xyz' and passwd='' or 'a'='a'";
很不幸的,所產生的 SQL 指令也會執行成功(因為 'a'='a' 是一定成立的),因而從資料庫中抓出多筆資料,這種剪接手法彷彿是在 SQL 指令中「灌注」一些惡意的字串,所以稱為「SQL Injection」。
如何避免 SQL Injection 呢?最簡單的作法,就是在取用客戶端送進來的資料前,先刪除所有可能造成問題的特殊字元,這些字元包括單引號(')、雙引號(")、問號(?)、星號(*)、底線(_)、百分比(%)、Ampersand(&)等,這些特殊字元都不應該出現在使用者輸入的資料中。另外,刪除特殊字元的動作務必要在伺服器端進行,因為用戶端的 JavaScript 表單驗證的檢查是只能防君子,不能防小人,別人只要做一個有相同欄位的網頁,就一樣可以呼叫你的 ASP 程式碼來取用資料庫,進而避開原網頁的表單驗證功能。
若要刪除這些危險字元,可以使用 JavaScript 的字串的 replace() 方法,或是使用 VBScript 的 Replace 函數,例如:
其原始碼列出如下:
在上述原始碼中,因為 Request("userid") 和 Request("passwd") 的資料是無法修改的,所以在取代前要先存到另一個個變數。由此範例可以知道,只要刪除使用者輸入字串中的所有單引號,就可以避免 SQL Injection 的問題。
事實上,可以形成 SQL Injection 的惡意字串還不少,但大部分是針對微軟的 SQL Server 資料庫來進行破壞。若有興趣,讀者可自行參考下列參考資料:
如果你到 Google 打入「登入」,再對需要登入的網站進行 SQL Injection 的測試,就應該可以找到一些不設防的網站。請千萬不要作惡,若找到這些不設防的網站,將下列文字寄給此網站的維護者(也可將副本寄給我):
敬啟者:
我們研習張智星老師的「JavaScript程式設計與應用」,對網路上的網頁進行 SQL Injection 的測試,發覺您的登入網頁(網址是 http://xxx.xxx.xxx)並無法對抗 SQL Injection 的入侵,只要帳號任意設定、密碼設定為「' or 'a'='a」,即可登入。
這是一封善意的信,我們僅測試是否可以登入,並未對資料進行任何修改,請查照,謝謝。
(請寫出你的全名)
謝謝您的努力,這些網站的管理者會感謝你們的善心!
JScript 程式設計與應用:用於伺服器端的 ASP 環境