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

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

本篇同步發佈在 Medium 平台上 Medium 連結

很多人都知道 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 所要表達的語意就不會變化!

comments powered by Disqus