SQL 的注入式攻擊
SQL 注入是一種攻擊方式,在這種攻擊方式中,惡意程式碼被插入到字串中,然後將該字串傳遞到 SQL Server 的例項以進行分析和執行。任何構成 SQL 語句的過程都應進行注入漏洞檢查,因為 SQL Server 將執行其接收到的所有語法有效的查詢。一個有經驗的、堅定的攻擊者甚至可以操作引數化資料。
SQL 注入的主要形式包括直接將程式碼插入到與 SQL 命令串聯在一起並使其得以執行的使用者輸入變數。一種間接的攻擊會將惡意程式碼注入要在表中儲存或作為後設資料儲存的字串。在儲存的字串隨後串連到一個動態 SQL 命令中時,將執行該惡意程式碼。
注入過程的工作方式是提前終止文字字串,然後追加一個新的命令。由於插入的命令可能在執行前追加其他字串,因此攻擊者將用註釋標記“--”來終止注入的字串。執行時,此後的文字將被忽略。
以下指令碼顯示了一個簡單的 SQL 注入。此指令碼通過串聯硬編碼字串和使用者輸入的字串而生成一個 SQL 查詢:
複製程式碼
var Shipcity;
ShipCity = Request.form. ("ShipCity");
var sql = "select * from OrdersTable where ShipCity = '" + ShipCity + "'";使用者將被提示輸入一個市縣名稱。如果使用者輸入 Redmond,則查詢將由與下面內容相似的指令碼組成:
SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond'
但是,假定使用者輸入以下內容:
Redmond'; drop table OrdersTable--
此時,指令碼將組成以下查詢:
SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond';drop table OrdersTable--'
分號 (;) 表示一個查詢的結束和另一個查詢的開始。雙連字元 (--) 指示當前行餘下的部分是一個註釋,應該忽略。如果修改後的程式碼語法正確,則伺服器將執行該程式碼。SQL Server 處理該語句時,SQL Server 將首先選擇 OrdersTable 中的所有記錄(其中 ShipCity 為 Redmond)。然後,SQL Server 將刪除 OrdersTable。
只要注入的 SQL 程式碼語法正確,便無法採用程式設計方式來檢測篡改。因此,必須驗證所有使用者輸入,並仔細檢查在您所用的伺服器中執行構造 SQL 命令的程式碼。本主題中的以下各部分說明了編寫程式碼的最佳做法。
驗證所有輸入
始終通過測試型別、長度、格式和範圍來驗證使用者輸入。實現對惡意輸入的預防時,請注意應用程式的體系結構和部署方案。請注意,設計為在安全環境中執行的程式可能會被複制到不安全的環境中。以下建議應被視為最佳做法:
對應用程式接收的資料不做任何有關大小、型別或內容的假設。例如,您應該進行以下評估:
如果一個使用者在需要郵政編碼的位置無意中或惡意地輸入了一個 10 MB 的 MPEG 檔案,應用程式會做出什麼反應?
如果在文字欄位中嵌入了一個 DROP TABLE 語句,應用程式會做出什麼反應?
測試輸入的大小和資料型別,強制執行適當的限制。這有助於防止有意造成的緩衝區溢位。
測試字串變數的內容,只接受所需的值。拒絕包含二進位制資料、轉義序列和註釋字元的輸入內容。這有助於防止指令碼注入,防止某些緩衝區溢位攻擊。
使用 XML 文件時,根據資料的架構對輸入的所有資料進行驗證。
絕不直接使用使用者輸入內容來生成 Transact-SQL 語句。
使用儲存過程來驗證使用者輸入。
在多層環境中,所有資料都應該在驗證之後才允許進入可信區域。未通過驗證過程的資料應被拒絕,並向前一層返回一個錯誤。
實現多層驗證。對無目的的惡意使用者採取的預防措施對堅定的攻擊者可能無效。更好的做法是在使用者介面和所有跨信任邊界的後續點上驗證輸入。
例如,在客戶端應用程式中驗證資料可以防止簡單的指令碼注入。但是,如果下一層認為其輸入已通過驗證,則任何可以繞過客戶端的惡意使用者就可以不受限制地訪問系統。
絕不串聯未驗證的使用者輸入。字串串聯是指令碼注入的主要輸入點。
在可能據以構造檔名的欄位中,不接受下列字串:AUX、CLOCK$、COM1 到 COM8、CON、CONFIG$、LPT1 到 LPT8、NUL 以及 PRN。
如果可能,拒絕包含以下字元的輸入。
輸入字元 在 Transact-SQL 中的含義
;
查詢分隔符。
'
字元資料字串分隔符。
--
註釋分隔符。
/* ... */
註釋分隔符。伺服器不對 /* 和 */ 之間的註釋進行處理。
xp_
用於目錄擴充套件儲存過程的名稱的開頭,如 xp_cmdshell。
使用型別安全的 SQL 引數
SQL Server 中的 Parameters 集合提供了型別檢查和長度驗證。如果使用 Parameters 集合,則輸入將被視為文字值而不是可執行程式碼。使用 Parameters 集合的另一個好處是可以強制執行型別和長度檢查。範圍以外的值將觸發異常。以下程式碼段顯示瞭如何使用 Parameters 集合:
複製程式碼
SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", conn);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add("@au_id",
SqlDbType.VarChar, 11);
parm.Value = Login.Text;在此示例中,@au_id 引數被視為文字值而不是可執行程式碼。將對此值進行型別和長度檢查。如果 @au_id 值不符合指定的型別和長度約束,則將引發異常。
在儲存過程中使用引數化輸入
儲存過程如果使用未篩選的輸入,則可能容易受 SQL Injection 攻擊。例如,以下程式碼容易受到攻擊:
複製程式碼
SqlDataAdapter myCommand =
new SqlDataAdapter("LoginStoredProcedure '" +
Login.Text + "'", conn);如果使用儲存過程,則應使用引數作為儲存過程的輸入。
在動態 SQL 中使用引數集合
如果不能使用儲存過程,您仍可使用引數,如以下程式碼示例所示:
複製程式碼
SqlDataAdapter myCommand = new SqlDataAdapter(
"SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", conn);
SQLParameter parm = myCommand.SelectCommand.Parameters.Add("@au_id",
SqlDbType.VarChar, 11);
Parm.Value = Login.Text;篩選輸入
篩選輸入可以刪除轉義符,這也可能有助於防止 SQL 注入。但由於可引起問題的字元數量很大,因此這並不是一種可靠的防護方法。以下示例可搜尋字串分隔符。
複製程式碼
private string SafeSqlLiteral(string inputSQL)
{
return inputSQL.Replace("'", "''");
}LIKE 子句
請注意,如果要使用 LIKE 子句,還必須對萬用字元字元進行轉義:
複製程式碼
s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]"); 在程式碼中檢查 SQL 注入
應檢查所有呼叫 EXECUTE、EXEC 或 sp_executesql 的程式碼。可以使用類似如下的查詢來幫助您標識包含這些語句的過程。此查詢檢查單詞 EXECUTE 或 EXEC 後是否存在 1 個、2 個、3 個或 4 個空格。
SELECT object_Name(id) FROM syscomments
WHERE UPPER(text) LIKE '%EXECUTE (%'
OR UPPER(text) LIKE '%EXECUTE (%'
OR UPPER(text) LIKE '%EXECUTE (%'
OR UPPER(text) LIKE '%EXECUTE (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%SP_EXECUTESQL%'
使用 QUOTENAME() 和 REPLACE() 包裝引數
在選擇的每個儲存過程中,驗證是否對動態 Transact-SQL 中使用的所有變數都進行了正確處理。來自儲存過程的輸入引數的資料或從表中讀取的資料應包裝在 QUOTENAME() 或 REPLACE() 中。請記住,傳遞給 QUOTENAME() 的 @variable 值的資料型別為 sysname,且最大長度為 128 個字元。
@variable 建議的包裝
安全物件的名稱
QUOTENAME(@variable)
字串 ≤ 128 個字元
QUOTENAME(@variable, '''')
字串 > 128 個字元
REPLACE(@variable,'''', '''''')
使用此方法時,可對 SET 語句進行如下修改:
--Before:
SET @temp = N'select * from authors where au_lname='''
+ @au_lname + N''''
--After:
SET @temp = N'select * from authors where au_lname='''
+ REPLACE(@au_lname,'''','''''') + N''''
由資料截斷啟用的注入
如果分配給變數的任何動態 Transact-SQL 比為該變數分配的緩衝區大,那麼它將被截斷。如果攻擊者能夠通過將意外長度的字串傳遞給儲存過程來強制執行語句截斷,則該攻擊者可以操作該結果。例如,以下指令碼建立的儲存過程容易受到由截斷啟用的注入攻擊。
CREATE PROCEDURE sp_MySetPassword
@loginname sysname,
@old sysname,
@new sysname
AS
-- Declare variable.
-- Note that the buffer here is only 200 characters long.
DECLARE @command varchar(200)
-- Construct the dynamic Transact-SQL.
-- In the following statement, we need a total of 154 characters
-- to set the password of 'sa'.
-- 26 for UPDATE statement, 16 for WHERE clause, 4 for 'sa', and 2 for
-- quotation marks surrounded by QUOTENAME(@loginname):
-- 200 – 26 – 16 – 4 – 2 = 154.
-- But because @new is declared as a sysname, this variable can only hold
-- 128 characters.
-- We can overcome this by passing some single quotation marks in @new.
SET @command= 'update Users set password=' + QUOTENAME(@new, '''') + ' where username=' + QUOTENAME(@loginname, '''') + ' AND password = ' + QUOTENAME(@old, '''')
-- Execute the command.
EXEC (@command)
GO
通過向 128 個字元的緩衝區傳遞 154 個字元,攻擊者便可以在不知道舊密碼的情況下為 sa 設定新密碼。
EXEC sp_MySetPassword 'sa', 'dummy', '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012'''''''''''''''''''''''''''''''''''''''''''''''''''
因此,應對命令變數使用較大的緩衝區,或直接在 EXECUTE 語句內執行動態 Transact-SQL。
使用 QUOTENAME(@variable, '''') 和 REPLACE() 時的截斷
如果 QUOTENAME() 和 REPLACE() 返回的字串超過了分配的空間,該字串將被自動截斷。以下示例中建立的儲存過程顯示了可能出現的情況。
CREATE PROCEDURE sp_MySetPassword
@loginname sysname,
@old sysname,
@new sysname
AS
-- Declare variables.
DECLARE @login sysname
DECLARE @newpassword sysname
DECLARE @oldpassword sysname
DECLARE @command varchar(2000)
-- In the following statements, the data stored in temp variables
-- will be truncated because the buffer size of @login, @oldpassword,
-- and @newpassword is only 128 characters, but QUOTENAME() can return
-- up to 258 characters.
SET @login = QUOTENAME(@loginname, '''')
SET @oldpassword = QUOTENAME(@old, '''')
SET @newpassword = QUOTENAME(@new, '''')
-- Construct the dynamic Transact-SQL.
-- If @new contains 128 characters, then @newpassword will be '123... n
-- where n is the 127th character.
-- Because the string returned by QUOTENAME() will be truncated,
-- it can be made to look like the following statement:
-- UPDATE Users SET password ='1234. . .[127] WHERE username=' -- other stuff here
SET @command = 'UPDATE Users set password = ' + @newpassword
+ ' where username =' + @login + ' AND password = ' + @oldpassword;
-- Execute the command.
EXEC (@command)
GO
因此,以下語句將把所有使用者的密碼都設定為在前面的程式碼中傳遞的值。
EXEC sp_MyProc '--', 'dummy', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678'
使用 REPLACE() 時,可以通過超出分配的緩衝區空間來強迫字串截斷。以下示例中建立的儲存過程顯示了可能出現的情況。
CREATE PROCEDURE sp_MySetPassword
@loginname sysname,
@old sysname,
@new sysname
AS
-- Declare variables.
DECLARE @login sysname
DECLARE @newpassword sysname
DECLARE @oldpassword sysname
DECLARE @command varchar(2000)
-- In the following statements, data will be truncated because
-- the buffers allocated for @login, @oldpassword and @newpassword
-- can hold only 128 characters, but QUOTENAME() can return
-- up to 258 characters.
SET @login = REPLACE(@loginname, '''', '''''')
SET @oldpassword = REPLACE(@old, '''', '''''')
SET @newpassword = REPLACE(@new, '''', '''''')
-- Construct the dynamic Transact-SQL.
-- If @new contains 128 characters, @newpassword will be '123...n
-- where n is the 127th character.
-- Because the string returned by QUOTENAME() will be truncated, it
-- can be made to look like the following statement:
-- UPDATE Users SET password='1234…[127] WHERE username=' -- other stuff here
SET @command= 'update Users set password = ''' + @newpassword + ''' where username='''
+ @login + ''' AND password = ''' + @oldpassword + '''';
-- Execute the command.
EXEC (@command)
GO
與 QUOTENAME() 一樣,可以通過宣告對所有情況都足夠大的臨時變數來避免由 REPLACE() 引起的字串截斷。應儘可能直接在動態 Transact-SQL 內呼叫 QUOTENAME() 或 REPLACE()。或者,也可以按如下方式計算所需的緩衝區大小。對於 @outbuffer = QUOTENAME(@input),@outbuffer 的大小應為 2*(len(@input)+1). 。使用 REPLACE() 和雙引號時(如上一示例),大小為 2*len(@input) 的緩衝區便已足夠。
以下計算涵蓋所有情況:
While len(@find_string) > 0, required buffer size =
round(len(@input)/len(@find_string),0) * len(@new_string)
+ (len(@input) % len(@find_string))
使用 QUOTENAME(@variable, ']') 時的截斷
當 SQL Server 安全物件的名稱被傳遞給使用 QUOTENAME(@variable, ']') 形式的語句時,可能發生截斷。下面的示例顯示了這種情況。
CREATE PROCEDURE sp_MyProc
@schemaname sysname,
@tablename sysname,
AS
-- Declare a variable as sysname. The variable will be 128 characters.
-- But @objectname actually must allow for 2*258+1 characters.
DECLARE @objectname sysname
SET @objectname = QUOTENAME(@schemaname)+'.'+ QUOTENAME(@tablename)
-- Do some operations.
GO
當您串聯 sysname 型別的值時,應使用足夠大的臨時變數來儲存每個值的最多 128 個字元。應儘可能直接在動態 Transact-SQL 內呼叫 QUOTENAME()。或者,也可以按上一部分所述來計算所需的緩衝區大小。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16436858/viewspace-503152/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- sql注入攻擊SQL
- SQL 注入攻擊SQL
- SQL隱碼攻擊-堆疊注入SQL
- 攻擊JavaWeb應用——3、sql注入(上)JavaWebSQL
- Mongodb注入攻擊MongoDB
- 反恐精英之動態SQL和SQL隱碼攻擊-SQL隱碼攻擊-SQL隱碼攻擊技術-語句注入SQL
- SQL隱碼攻擊式攻擊掃描器SQL
- php安全程式設計—sql注入攻擊PHP程式設計SQL
- sql注入定義、原理、攻擊和防護SQL
- SQL隱碼攻擊之常見注入的步驟④SQL
- SQL隱碼攻擊原理——萬能密碼注入SQL密碼
- SQL隱碼攻擊--sqlmap自動化注入工具SQL
- SQL隱碼攻擊 - 手工注入sqli-labsSQL
- 六個建議防止SQL隱碼攻擊式攻擊SQL
- 哪個報表工具能抵擋 SQL 注入攻擊SQL
- SQL隱碼攻擊之字元型和數字型注入SQL字元
- ASP上兩個防止SQL隱碼攻擊式攻擊FunctionSQLFunction
- React 防注入攻擊 XSS攻擊 (放心大膽的用吧)React
- 滲透測試網站sql注入攻擊與防護網站SQL
- SQL隱碼攻擊之二次注入(sql-lab第24關)SQL
- 反恐精英之動態SQL和SQL隱碼攻擊-SQL隱碼攻擊-防衛SQL隱碼攻擊-顯式格式化模型SQL模型
- SQL隱碼攻擊之二次,加解密,DNS等注入SQL解密DNS
- 封神臺 SQL隱碼攻擊 靶場 (貓舍)手動注入SQL
- XSLT 服務端注入攻擊服務端
- SQL隱碼攻擊速查表(下)與Oracle注入速查表SQLOracle
- SQL隱碼攻擊SQL
- SQL隱碼攻擊的例子SQL
- 【SQL Server】--SQL隱碼攻擊SQLServer
- 非掃描式定位攻擊域內SQL ServerSQLServer
- 以太坊智慧合約call注入攻擊
- 在ASP.NET中防止注入攻擊ASP.NET
- 報告稱超 6 成 Web 應用程式攻擊來自 SQL 注入WebSQL
- 開源新聞速遞:Ubuntu 官方論壇遭遇了 SQL 注入攻擊UbuntuSQL
- 反恐精英之動態SQL和SQL隱碼攻擊-SQL隱碼攻擊-SQL隱碼攻擊技術-語句修改SQL
- 攻擊JavaWeb應用[3]-SQL隱碼攻擊[1]JavaWebSQL
- 攻擊JavaWeb應用[4]-SQL隱碼攻擊[2]JavaWebSQL
- 反恐精英之動態SQL和SQL隱碼攻擊-SQL隱碼攻擊SQL
- 分散式拒絕服務攻擊 DDoS攻擊分散式