再簡單的應用程式也可能需要持久地儲存與取回資料。做法很多,其中最常見的一種是使用關聯式資料庫。資料庫的好處不少,其中之一是能對資料下查詢,執行複雜的分組與分析。
定義關聯式資料庫查詢的事實標準是 SQL(Structured Query Language)。這是一種以文字為基礎的語言,用來指定要讀取哪些資料表、以及如何過濾資料以取得應用程式想要的結果。而只要用上文字語言,就會有「用字串操作來組查詢」的誘惑——這很容易導致與命令注入雷同的漏洞:這次攻擊者插入未跳脫資料的地方不是命令列,而是會送到資料庫執行的 SQL 查詢。
def process_authentication()
{
string username = read_string();
string password = read_string();
string sql = "SELECT password FROM user_table WHERE user = '" + username "'";
return run_query(sql) == password;
}程式從網路讀取使用者名稱與密碼,用字串串接組出一句 SELECT,去撈該使用者對應的密碼,最後比對網路送來的密碼是否相符。
這個漏洞很容易利用。在 SQL 中,字串必須以單引號包住,才不會被當成 SQL 語句中的命令。若協定送來的使用者名稱裡嵌入了一個單引號,攻擊者就能提前終止被引號包住的字串,進而把新的命令注入到 SQL 查詢中。例如用一句
UNION SELECT,就能讓查詢回傳一個任意的密碼值,從而繞過應用程式的身分驗證。
SQL 注入甚至可以導向遠端程式碼執行:
- Microsoft SQL Server 的
xp_cmdshell函式允許執行作業系統命令(雖然預設是停用的)。 - Oracle 的資料庫甚至允許上傳任意 Java 程式碼。
你也會遇到直接把原始 SQL 查詢透過網路傳遞的應用程式。而且即使某個協定本來就不是設計來控制資料庫的,它仍然很有機會被利用來存取底層的資料庫引擎。