Home >> Blog >> 理解SQL injection攻擊模式

理解SQL injection攻擊模式

概述

SQL injection攻擊包括通過從客戶端到應用程序的輸入資料插入或“injection”SQL 查詢。成功的 SQL injection漏洞可以從資料庫中讀取敏感資料、修改資料庫資料(插入/更新/刪除)、對資料庫執行管理操作(例如關閉 DBMS)、恢復 DBMS 文件中存在的給定文件的內容系統並在某些情況下向操作系統發出命令。SQL injection攻擊是一種injection攻擊,其中 SQL 命令被injection到資料平面輸入中,以影響預定義 SQL 命令的執行。

威脅建模

  • SQL injection攻擊允許攻擊者偽造身份、篡改現有資料、導致交易無效或更改餘額等否認問題、允許完全披露系統上的所有資料、破壞資料或使其不可用,並成為系統管理員資料庫服務器。
  • 由於舊功能接口的流行,SQL injection在 PHP 和 ASP 應用程序中非常常見。由於可用的編程接口的性質,J2EE 和 ASP.NET 應用程序不太可能輕易利用 SQL injection。
  • SQL Injection 攻擊的嚴重程度受限於攻擊者的技巧和想像力,在較小程度上受限於縱深防禦對策,例如低權限連接資料庫服務器等。通常,將 SQL injection視為高影響嚴重性。

描述

SQL injection攻擊發生在以下情況:

  1. 意外資料從不受信任的來源進入程序。
  2. 資料用於動態構造 SQL 查詢,最常遇到的情況就是跑SEO客戶的wordpress網站,如無定期update外掛,頁面就有可能被注入無關的連結。

主要後果是:

  • 機密性:由於 SQL 資料庫通常保存敏感資料,因此丟失機密性是 SQL injection漏洞的常見問題。
  • 身份驗證:如果使用糟糕的 SQL 命令來檢查用戶名和密碼,則可能以以前不知道密碼的另一個用戶身份連接到系統。
  • 授權:如果授權資訊保存在 SQL 資料庫中,則可以通過成功利用 SQL injection漏洞來更改此資訊。
  • 完整性:就像可以讀取敏感資訊一樣,也可以通過 SQL injection攻擊來更改甚至刪除此資訊。

風險因素

受影響的平台可以是:

  • 語言:SQL
  • 平台:任何(需要與 SQL 資料庫交互)

SQL injection已成為資料庫驅動網站的常見問題。該漏洞很容易被檢測到,也很容易被利用,因此,任何具有最小用戶群的站點或軟件包都可能受到此類企圖攻擊。

本質上,攻擊是通過將元字符放入資料輸入中,然後將 SQL 命令放入控制平面中來完成的,這在以前並不存在。這個缺陷取決於 SQL 沒有真正區分控制平面和資料平面這一事實。

例子

範例 1

在 SQL 中: select id, firstname, lastname from authors

如果提供:Firstname: evil'ex和Lastname: Newman

查詢字符串變為:

select id, firstname, lastname from authors where firstname = 'evil'ex' and lastname

='newman'

資料庫嘗試運行為:

Incorrect syntax near il' as the database tried to execute evil.

上述 SQL 語句的安全版本可以用 Java 編碼為:

String firstname = req.getParameter("firstname");
String lastname = req.getParameter("lastname");
// FIXME: do your own validation to detect attacks
String query = "SELECT id, firstname, lastname FROM authors WHERE firstname = ? and lastname = ?";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, firstname );
pstmt.setString( 2, lastname );
try
{
ResultSet results = pstmt.execute( );

範例 2

以下 C# 程式碼動態構造並執行 SQL 查詢,以搜索與指定名稱匹配的項目。該查詢將顯示的項目限制為所有者與當前經過身份驗證的用戶的用戶名匹配的項目。

...
string userName = ctx.getAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = "'"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
sda = new SqlDataAdapter(query, conn);
DataTable dt = new DataTable();
sda.Fill(dt);
...

此程式碼打算執行的查詢如下:

SELECT * FROM items
WHERE owner =
AND itemname = ;

但是,由於查詢是通過連接常量基本查詢字符串和用戶輸入字符串來動態構造的,因此只有在itemName不包含單引號字符時查詢才能正確運行。如果用戶名為 wiley 的攻擊者輸入字符串"name' OR
'a'='a"for itemName,則查詢變為以下內容:

SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';

添加OR 'a'='a'條件會導致 where 子句始終評估為 true,因此查詢在邏輯上等同於更簡單的查詢:

SELECT * FROM items;

查詢的這種簡化允許攻擊者繞過查詢僅返回經過身份驗證的用戶擁有的項目的要求;該查詢現在返回存儲在 items 表中的所有條目,而不管它們指定的所有者。

範例 3

此範例檢查傳遞給範例 1 中構造和執行的查詢的不同惡意值的影響。如果用戶名為 hacker 的攻擊者輸入字符串"name'); DELETE FROM items; --" for itemName,則查詢變為以下兩個查詢:

SELECT * FROM items
WHERE owner = 'hacker'
AND itemname = 'name';

DELETE FROM items;

--'

許多資料庫服務器,包括 Microsoft® SQL Server 2000,允許同時執行多個用分號分隔的 SQL 語句。雖然這種攻擊字符串會導致 Oracle 和其他不允許批量執行以分號分隔的語句的資料庫服務器中的錯誤,但在允許批量執行的資料庫中,這種類型的攻擊允許攻擊者對資料庫執行任意命令.

請注意尾部的連字符 ( --) 對,它向大多數資料庫服務器指定語句的其餘部分將被視為註釋而不執行。在這種情況下,註釋字符用於刪除修改後的查詢中留下的尾隨單引號。在不允許以這種方式使用註釋的資料庫中,使用類似於範例 1 所示的技巧仍然可以使一般攻擊有效。如果攻擊者輸入字符串"name'); DELETE FROM items;

SELECT \* FROM items WHERE 'a'='a",將創建以下三個有效語句:

SELECT * FROM items
WHERE owner = 'hacker'
AND itemname = 'name';

DELETE FROM items;

SELECT * FROM items WHERE 'a'='a';

防止 SQL injection攻擊的一種傳統方法是將它們作為輸入驗證問題進行處理,或者僅接受安全值允許列表中的字符,或者識別並轉義潛在惡意值的拒絕列表。允許列表是強制執行嚴格的輸入驗證規則的一種非常有效的方法,但參數化的 SQL 語句需要較少的維護,並且可以在安全性方面提供更多的保證。幾乎總是這樣,拒絕列表充滿了漏洞,使其無法有效防止 SQL injection攻擊。例如,攻擊者可以:

  • 未引用的目標字段
  • 想辦法繞過對某些轉義元字符的需求
  • 使用存儲過程隱藏injection的元字符

在 SQL 查詢的輸入中手動轉義字符會有所幫助,但它不會使您的應用程序免受 SQL injection攻擊。

另一種通常提出的用於處理 SQL injection攻擊的解決方案是使用存儲過程。儘管存儲過程可以防止某些類型的 SQL injection攻擊,但它們無法防禦許多其他類型的攻擊。例如,以下 PL/SQL 過程容易受到與第一個範例中所示相同的 SQL injection攻擊。

procedure get_item (
itm_cv IN OUT ItmCurTyp,
usr in varchar2,
itm in varchar2)
is
open itm_cv for ' SELECT * FROM items WHERE ' ||
'owner = '''|| usr ||
' AND itemname = ''' || itm || '''';
end get_item;

存儲過程通常通過限制可以傳遞給其參數的語句類型來幫助防止 SQL injection攻擊。但是,有很多方法可以繞過這些限制,並且仍然可以將許多有趣的語句傳遞給存儲過程。同樣,存儲過程可以防止一些漏洞,但它們不會使您的應用程序免受 SQL injection攻擊。