巴哈姆特

哈啦區 程式設計板
查看全文
摘喵喵 (kanade86514) 2017-05-20 00:28:24
#1
現今網路世界的資源非常豐富,網頁後端開發的入門門檻越來越低。雖充斥著大量的教學文章,但這些文章為了能以最簡單的方式說明,大多都忽略最基礎的資訊安全。

因此我特地撰寫此篇文章,期望能宣達給網頁後端的新血,告訴大家如何保障自己開發的網站的安全,降低遭到黑帽駭客攻擊的風險。

* 此篇介紹將以最廣為人知、容易入門的 PHP 語言搭配 MySQL 為例。




何謂 Web Security (網頁安全)

泛指針對網頁、網站上的資訊安全,涵蓋程式漏洞、邏輯漏洞、資訊洩漏等。



何謂 SQL Injection

這是本篇文章的重點,相信有接觸網頁後端的朋友對 SQL 並不陌生,SQL 全稱為「Structured Query Language」,就是用來操作資料庫、存取出所需資料的一套「查詢語言」。

在資訊安全中有一項針對資料庫的漏洞攻擊稱作 SQL Injection,不僅僅是網頁程式,只要是有和資料庫進行連結的任何程式都可能產生此項漏洞。

此漏洞的成因很簡單,就是允許使用者輸入的字串在代入 SQL 查詢語句時,沒有過濾非法字元,導致字串成為查詢語句的一部分,達到讓攻擊者能執行任意 SQL 語句。

假設現在某個資料庫中,共有兩個資料表demo、users,其結構如下:


資料表 demo


資料表 users


其中 demo 共有 2 筆記錄,users 則有 1 筆記錄,兩個資料表是毫無關聯的。

資料表 demo


資料表 users


現在我擁有一個網頁,它只能隨意存取 demo 資料表內的任何記錄。



假如輸入不存在的 id,會得空白的結果。


不知道各位是否注意到問題?問題在於這個網頁「預期使用者輸入的 id 一定是整數」,所以當使用者輸入非整數資料時,會發生錯誤,得到完全空白的結果。


因為預期 id 是整數的關係,網頁直接將 id 的參數串接進 SQL 語句中,當攻擊者嘗試輸入「-9527 OR 1=1」,發生了有趣的事情。


為何明明是錯誤的 id,卻出現了資料?
原因非常簡單,因為「OR 1=1」變成 SQL 語句的一部分了。

這是處裡 SQL 語句的 PHP 的部份:
$sql = 'SELECT * FROM demo WHERE id='.$id;
要特別注意到 $id 會直接接受 $_GET['id'] 的值,不會被修改。

原本預期完成的查詢語句應該長這個樣子:
/demo.php?id=1
SELECT * FROM demo WHERE id=1

但是當攻擊者傳入 id=-9527 OR 1=1時,SQL 語句會變成:
/demo.php?id=-9527 OR 1=1
SELECT * FROM demo WHERE id=-9527 OR 1=1

因為 OR 後方的條件永遠會成立,才會導致明明 id 不存在卻能出現結果。
假如配上 UNION 語句,與猜測對應的資料表,駭客可以輕易取得任何資料。
/demo.php?id=-1 UNION SELECT id,username,password FROM users
SELECT *
FROM demo
WHERE id=-1 UNION SELECT id,username,password FROM users



不單單只是如此,若資料庫擁有的權限過高,駭客甚至可以竄改首頁、控制整台伺服器主機,只要透過 INTO OUTFILE 相關指令即可。

而依據不同伺服器的設定,錯誤的查詢語句也會有不同反應,例如空白頁、PHP Warning等。

所以 SQL Injection 大致上能以此分成以下幾類:
  • In-Band:
    • Union-Based SQL Injection - 透過 Union 語句的攻擊
    • Error-Based SQL Injection - 利用錯誤訊息顯示子查詢結果的攻擊
  • Blind:
    • Boolean-Based SQL Injection - 透過條件式是否相符猜測資料字元的攻擊
    • Time-Based SQL Injection - 同上,但改以延遲時間來作判定
  • Out-of-Band
更多資訊請參考:https://www.acunetix.com/websitesecurity/sql-injection2/
在竊取資料方面, In-Band 類型的是最危險的,因為可以直接顯示資料;而 Out-of-Band 其中一種方式可能允許進行各種插入、刪除所有資料表的動作,所以風險甚高。



如何防範 SQL Injection?

一般人直覺上會選擇使用 str_replace、strpos 等函式偵測、過濾惡意字元,例如「 ' 」、「 " 」、「  (空白)」等,甚至是過濾關鍵字「SELECT」、「UNION」、「AND」。但這麼做其實完全不足以防範惡意的駭客,因為空白、單引號的字元繞過是十分容易的,甚至大部分的 SQL Injection 測試工具都很少會使用單引號作測試,作者我在這邊提供兩種參考的防範方式。

1. mysqli_real_escape_string 函式 (不推薦)

只要在參數串入 SQL 語句之前,用此函式先將欲串入的參數作過濾,它會將所有 SQL 的關鍵字元全部替換成跳脫字元,使其在 SQL 查詢中成為普通的字串,而非語句的一部分。從結果看出來,real_escape_string 把單引號給過濾成「\'」的字串。



但這只能應付資料庫中屬於字串的欄位,對於數字的欄位可以再搭配 is_numeric 函式來使用。


若該參數不是數字,則結果會為 false,以此來判斷是否參雜非法字元。


2. Prepared Statement (推薦)

這是最推薦的作法,因為可以從根本上澈底解決 SQL Injection 的問題,除非是 PHP 官方本身附帶的函式爆出漏洞。Prepared Statement 會替 SQL 語句進行預處理,再利用它提供的 bindValue 或 bindParam 函式將欲查詢的參數的值或變數綁定上去,底層查詢時,其參數會保證作為數值傳遞,不可能成為 SQL 語句的一部分,也因此就不會產生 SQL Injection 的問題。


即使輸入錯誤的參數,也不會發生錯誤,只會查詢失敗而得到空結果而已。

大部分的網頁後端語言都有提供相對應類似的 Prepared Statement 機制,所以十分建議無論用何種語言撰寫網頁後端,都應該花時間找找有無類似的功能可使用。



以上就是我的心得和建議提供給網頁後端的新人們,希望能藉此提升各位對網頁安全的意識。
若有其他大大覺得有錯誤的地方也歡迎告知,讓我能及時修正。

看較舊的 1 則留言

孤星有淚: B2 2017-05-20 21:43

請問有高階注入的介紹嗎?

摘喵喵: B3 2017-05-21 17:10

> 孤星有淚:此篇目的是提供給後端開發新人,如果是進階的注入技巧就是純資安範疇,要看未來有沒有機會發文談這方面,現在暫時沒有唷。

存在且唯一: B4 2017-07-19 12:44

請問可以介紹 NoSQL的攻擊手法嗎?

存在且唯一: B5 2017-07-19 13:01

目前只看過一個例子db.users.find({"username":"admin", "password":{"$ne":"1"}});

摘喵喵: B6 2017-07-20 22:24

NoSQL Injection 我還沒研究過,不過感覺蠻有趣的,若有得出心得再來發文XDD

開啟 APP

face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】