(SQL) 爲什麼參數化查詢可以防止SQL Injection/如何觀看Execution Plan上的差異

Jerry Wu
Jan 12, 2021

--

from https://medium.com/@charithra/introduction-to-sql-injections-8c806537cf5d

很多人都知道SQL Injection的攻擊原理,也知道參數化查詢可以防止SQL Injection,但是參數化查詢爲什麼能防止SQL Injection呢?本篇文章主要說明的正是這個問題,在了解主要原因(不使用組字串方式執行SQL)後,將帶你了解如何觀看Execution Plan上的差異

主要原因(不使用組字串方式執行SQL)

像上述這樣丟進SQL Server的參數是不會被當作 SQL 語法去執行的,因此就算使用者輸入的參數有注入的語法也不會因此執行成功!

在使用參數化查詢語法的情況下,SQL Server會先將 SQL 語句進行編譯,之後再把使用者輸入的參數丟進先前編譯的 SQL 語句再次執行。而預處理的 SQL 語句除了防範 SQL injection 更提升了效能。

接著將來看看使用不同的查詢在SQL Server Execution Plan上的差異!

如何觀看Execution Plan上的差異

首先,我們要了解SQL Server收到一個指令後會做些什麼事情:

實際上可能有些差異,但大致的步驟如下,

SQL Server收到SQL指令 --> 進行語法解析、語意分析 --> 編譯SQL生成執行計劃 -->選擇執行計劃 -->執行執行計劃

接著我們就來創建一張表並實際來看看以下三種查詢在SQL Server是怎麼被執行的:

(1) 正常的查詢,執行有漏洞的SP

(2) SQL Injection查詢,執行有漏洞的SP

(3) 參數化查詢,執行使用參數化寫法的SP

首先創建一張USERS表,語法如下:

並且寫入一些測試資料,語法如下:

執行完上述語法後,此時資料庫內應會有這五筆資料:

接下來分別測試三種查詢方式,並觀察其Execution Plan,此處將使用Store Procedure做示範:

首先,分別建立兩個Store Procedure

WrongSP (使用組字串的方式最後使用EXEC執行)

CorrectSP (使用參數化查詢方式)

(1) 正常的查詢,執行有漏洞的SP

(2) SQL Injection查詢,執行有漏洞的SP

(3) 參數化查詢,執行使用參數化寫法的SP

若不清楚物件類型的區別這邊幫大家擷取Microsoft的文件,該欄位內容如下:

from https://docs.microsoft.com/zh-tw/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-cached-plans-transact-sql?view=sql-server-ver15

以上試驗應該可以看出三種查詢其在執行Execution Plan上是有差異的,

(1) 正常的查詢:執行三次WrongSP,執行計畫結果為 → 使用3次Proc,另外使用2次Adhoc,我再舉一個例子可以更明顯看出原因,

一樣都是執行三次WrongSP,我只改變了查詢條件的一個字便使用了三個不同的Execution Plan,表示三次查詢皆沒有重用Proc的Execution Plan!

(2) SQL Injection查詢:執行三次WrongSP,執行計畫結果為 → 使用3次Proc,另外使用2次Adhoc,會有這樣的結果在剛剛第一點的例子也解釋了這是因為沒有重用Proc的Execution Plan!

(3) 參數化查詢:執行三次CorrectSP,執行計畫結果為 → 使用3次Proc,這表示了什麼?就是Proc的Execution Plan被重用了!

經過上述說明,可以看到在第二個例子(SQL Injection查詢)中,SQL表示的含義是找出(UserName='' 且 CellPhoneNumber='') 或者1=1 的所有User,這段SQL Injection語法改變了原來自己的初衷,也就是SQL的語意發生了改變,而為什麼發生了改變呢?原因是沒有重用先前建立好的Execution Plan(Proc),造成SQL Server對注入後的SQL語法重新進行了編譯、生成Execution Plan並執行新的Execution Plan。

結語

參數化查詢爲什麼能防止SQL Injection,主要是因為不使用「組字串」的方式來執行 SQL。而在第二部分觀看Execution Plan上的差異,可以得知執行計畫未被更改(也就是重用Execution Plan),是可以防止SQL Injection的

而Insert/Update/Delete也是一樣的道理,只要可以重用Execution Plan,SQL所要表達的語意就不會變化!

謝謝你的閱讀!如果有任何回饋或疑問,歡迎留言給我!
如果對我的文章有興趣,請不吝按下Follow & Clap!
每個月都會認真更新文章唷😊 千萬別錯過了~

--

--

Jerry Wu

Full-Stack Engineer,熱愛接觸Web前後端、DevOps相關技術與知識,喜歡分享、旅遊和桌球🏓