同事聊到小孩唸資工系,學校出的作業要寫 DB 存取相關的程式,我萌生一個大哉問:學校有教 SQL Injection 知識嗎?(延伸閱讀:你的網站正在裸奔嗎?) 便在臉書上開了一個不專業民調,想問問資訊科系出身的朋友們,是否在學校就學過知道 SQL Injection?

首先,我想要謝謝每一個願意花時間勾問卷及留言滿足我好奇心的每一個你! 我這個人生性懶惰,所以就不一一回覆了。我想先讓你們知道,也許對你來說只是一個小小的舉動,但對我來說意義很重大。(仿蕾神體)

不專業分析,45% 的朋友在學期間便已認識 SQL Injection,有些人在資料庫、資料結構課學到,有相比例的同學是在資安課學到(甚至要 SQL Injection 成功才算及格),也有朋友在網頁程式設計學到。但也有 35% 的朋友表示學校有教資料庫存取,但沒提過 SQL Injection。另外也有朋友表示,求學年代太早,還沒有 SQL Injection 這種東西。再來,還有一點值得一提,不少人學到的當下還無法體會其重要性。(個人以為教學時該搭配阿魯巴示意圖以加深印象)

SQL Injection 概念是何時被提出來的?依據維基百科 , SQL Injection 在 1998 年第一次被提出,距今已 24 年,乍看還蠻年輕的,但仔細推敲,雖然 SQL 語法起源於 1970 年代, 但直到 90 年代網站興起,在使用者跟資料庫間形成緊密連結,資料庫這才飛入尋常百姓家,而幾年之後,馬上就有人想出透過這條管道幹壞事的點子。

既然聊到這個,我大方承認:我小時侯寫過 SQL Injection 的網站,超級經典的那種。

1998 年,入行第二年,剛學會 VB/VBScript 的菜鳥奉命接了一個網站開發專案 - 要在 UNIX 寫網頁介面連 Oracle 查資料! 出道的第一個作品,不是 ASP + SQL,而是 UNIX 網站連 Oracle,現在回頭看還真奇妙。

沒在 UNIX 寫過程式,不知道 Oracle 是圓是扁,最要命的是當年 Internet 興起沒多久,Google 搜尋引擎還在草創第二年, 不像今天各式教學、程式庫、範例、茶包文滿坑滿谷,Stackoverflow 還要等 10 年才誕生,當年真是初生之犢不畏虎,帶著滿滿自信,就把案子接下來了。(所謂「不知者無懼」吧 😄)

當時查 UNIX 跑程式連 Oracle 的做法好像只找到 C 跟 Java (JDBC),兩種都超出我的能力範圍,我是公司唯一在寫網站跟資料庫程式的工程師, 所以也沒學長前輩可問... 即將溺水之際,我抓到一根稻草 - Perl!!

Perl 跨平台,可裝在 UNIX 主機,而 Perl CGI 可以寫網站,簡單易學, 靠強大的 Regular Expression 解析 GET/POST 內容取出參數,再穿插變數 print 回傳,一個動態網頁就完成了:

#!/usr/bin/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "POST")
{
   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
   $buffer = $ENV{'QUERY_STRING'};
}
@pairs = split(/&/, $buffer);
foreach $pair (@pairs)
{
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}
$name = $FORM{name};

print "Content-type:text/html\r\n\r\n";
print "<html>";
print "<head>";
print '  <meta charset="utf-8">';
print '  <title>Greeting</title>';
print "</head>";
print "<body>";
print "  <h2>Hello, $name</h2>";
print "</body>";
print "</html>";

1;

能接收到 HTML 表單送出的參數,剩下的問題是 - 該怎麼連 ORACLE 查資料庫?

我想到一個好點子:UNIX 主機裝有 Oracle 客戶端工具 sqlplus,寫成 sqlplus user/pass@db_name @script_file 可以連上 Oracle 執行 script_file 文字檔的 SQL 指令輸出到螢幕上,搭配 SPOOL 指令可將查詢結果存入文字檔;而 Perl 可以透過 system 指令執行外部程式。

於是我把兩邊串在一起:

  1. Perl 接收 GET 傳入的查詢條件參數
  2. Perl 將傳入的參數值寫進 SQL 指令中的 WHERE SomeCol = '$colValue' 寫成 yyyyMMddhhmmss.sql (警告:千萬別這麼做,這是標準 SQL Injection,會死!!), 並指定將結果輸出到 yyyyMMddhhmmss.txt
  3. Perl 用 system 函式執行 sqlplus user/pass@db_name @yyyyMMddhhmmss.sql
  4. sqlplus 連上 Oracle 執行查詢,將結果寫入 yyyyMMddhhmmss.txt
  5. Perl 解析 yyyyMMddhhmmss.txt 轉為 HTML Table 印出來

以上犯了兩個濤天大錯:

  1. 把帳號密碼明碼寫在程式裡
    只要取得主機、個人電腦甚至備份磁碟機的讀取權限,就能拿到密碼
  2. 直接用使用者輸入內容組成 SQL 指令
    有心人只需動點手腳便能偷看資料、竄改刪除資料,甚至藉此植入後門木馬,網站跟在裸奔沒兩樣

所幸,1998 年 SQL Injection 攻擊手法尚未流傳開來,網站順利驗收、收款,平安地運轉了兩年,之後改寫成 ASP + SQL 架構。

但在這個案子之後,我又寫了一些內含 SQL Injection 的網站(那時還不知這樣寫有什麼問題),直到有天參加完一場研討會。

永遠記得我接種疫苗的那一天... 當時,身為天真無邪的菜鳥程式師,在研討會中聽講師介紹了 SQL Injection,並打趣地說,在竹科的場次,講完這一段,通常就有一批聽眾神色緊張地衝出會場,趕回公司改程式去了。當時,台下的我,雖然克制住打道回公司的衝動,但接連而來的是三天少吃少喝少睡的日子,將過去做過專案裡所有的 ASP、VB COM 都掃過一次,之後再通知客戶更新程式,結束這場驚魂。而從此之後,每當要寫 SQL 指令時,都會銘記「嚴禁將外界輸入內容串接進 SQL 指令」這項天條,不再犯錯。

這是十幾年前的事了,同樣的事發生在現在就很難平安下莊了。Internet 上每天都有無數自動化程式亂槍打鳥,隨機找網站送出包含 SQL Injection 參數的請求進行試探,一旦判斷網站可能存在漏洞,便會吸來駭客好好研究你的網站... 網站帶著 SQL Injection 上線,有可能不到兩天就淪陷。

24 年過去,雖然目前大部分的程式開發人員都已認識 SQL Injection,偶爾在論壇或新手發問時仍會發現它的蹤跡。這有賴大家一起努力。看到同學同事朋友寫出 SQL Injection,別客氣,請拿拖鞋從後腦杓巴下去,讓他這輩子不會再忘記。共勉之。

寫完文章,一時興起去翻了光碟博物館。幹! 檔案居然還在...


Comments

# by pion

wa...so cool.

# by 78

這麼久還能保留 優秀

# by Ike

「含天」…如果不是「寒天」,那就是「今天」了吧

# by ByTIM

良好的備份,有助於了解黑歷史!?(誤

# by oaww

真的厲害的是檔案管理!

# by 小黑

讚嘆黑哥

Post a comment