如何有效防范SQL注入-來自微軟
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
SQL 注入是一種攻擊方式,在這種攻擊方式中,惡意代碼被插入到字符串中,然后將該字符串傳遞到 SQL Server 的實(shí)例以進(jìn)行分析和執(zhí)行。任何構(gòu)成 SQL 語句的過程都應(yīng)進(jìn)行注入漏洞檢查,因?yàn)?SQL Server 將執(zhí)行其接收到的所有語法有效的查詢。一個(gè)有經(jīng)驗(yàn)的、堅(jiān)定的攻擊者甚至可以操作參數(shù)化數(shù)據(jù)。 SQL 注入的主要形式包括直接將代碼插入到與 SQL 命令串聯(lián)在一起并使其得以執(zhí)行的用戶輸入變量。一種間接的攻擊會(huì)將惡意代碼注入要在表中存儲(chǔ)或作為元數(shù)據(jù)存儲(chǔ)的字符串。在存儲(chǔ)的字符串隨后串連到一個(gè)動(dòng)態(tài) SQL 命令中時(shí),將執(zhí)行該惡意代碼。 注入過程的工作方式是提前終止文本字符串,然后追加一個(gè)新的命令。由于插入的命令可能在執(zhí)行前追加其他字符串,因此攻擊者將用注釋標(biāo)記“--”來終止注入的字符串。執(zhí)行時(shí),此后的文本將被忽略。 以下腳本顯示了一個(gè)簡單的 SQL 注入。此腳本通過串聯(lián)硬編碼字符串和用戶輸入的字符串而生成一個(gè) SQL 查詢: var Shipcity; 用戶將被提示輸入一個(gè)市縣名稱。如果用戶輸入 Redmond,則查詢將由與下面內(nèi)容相似的腳本組成: SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond' 但是,假定用戶輸入以下內(nèi)容: Redmond'; drop table OrdersTable-- 此時(shí),腳本將組成以下查詢: SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond';drop table OrdersTable--' 分號(hào) (;) 表示一個(gè)查詢的結(jié)束和另一個(gè)查詢的開始。雙連字符 (--) 指示當(dāng)前行余下的部分是一個(gè)注釋,應(yīng)該忽略。如果修改后的代碼語法正確,則服務(wù)器將執(zhí)行該代碼。SQL Server 處理該語句時(shí),SQL Server 將首先選擇 OrdersTable 中的所有記錄(其中 ShipCity 為 Redmond)。然后,SQL Server 將刪除 OrdersTable。 只要注入的 SQL 代碼語法正確,便無法采用編程方式來檢測篡改。因此,必須驗(yàn)證所有用戶輸入,并仔細(xì)檢查在您所用的服務(wù)器中執(zhí)行構(gòu)造 SQL 命令的代碼。本主題中的以下各部分說明了編寫代碼的最佳做法。 驗(yàn)證所有輸入 始終通過測試類型、長度、格式和范圍來驗(yàn)證用戶輸入。實(shí)現(xiàn)對惡意輸入的預(yù)防時(shí),請注意應(yīng)用程序的體系結(jié)構(gòu)和部署方案。請記住,為在安全環(huán)境下運(yùn)行而設(shè)計(jì)的程序可能被復(fù)制到不安全的環(huán)境中。以下建議應(yīng)被視為最佳做法:
如果可能,拒絕包含以下字符的輸入。
使用類型安全的 SQL 參數(shù)SQL Server 中的 Parameters 集合提供了類型檢查和長度驗(yàn)證。如果使用 Parameters 集合,則輸入將被視為文字值而不是可執(zhí)行代碼。使用 Parameters 集合的另一個(gè)好處是可以強(qiáng)制執(zhí)行類型和長度檢查。范圍以外的值將觸發(fā)異常。以下代碼段顯示了如何使用 Parameters 集合: SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", conn); 在此示例中,@au_id 參數(shù)被視為文字值而不是可執(zhí)行代碼。將對此值進(jìn)行類型和長度檢查。如果 @au_id 值不符合指定的類型和長度約束,則將引發(fā)異常。 在存儲(chǔ)過程中使用參數(shù)化輸入存儲(chǔ)過程如果使用未篩選的輸入,則可能容易受 SQL 注入攻擊。例如,以下代碼容易受到攻擊: SqlDataAdapter myCommand = 如果使用存儲(chǔ)過程,則應(yīng)使用參數(shù)作為存儲(chǔ)過程的輸入。 在動(dòng)態(tài) SQL 中使用參數(shù)集合如果不能使用存儲(chǔ)過程,您仍可使用參數(shù),如以下代碼示例所示: SqlDataAdapter myCommand = new SqlDataAdapter( 篩選輸入篩選輸入可以刪除轉(zhuǎn)義符,這也可能有助于防止 SQL 注入。但由于可引起問題的字符數(shù)量很大,因此這并不是一種可靠的防護(hù)方法。以下示例可搜索字符串分隔符。 LIKE 子句請注意,如果要使用 LIKE 子句,還必須對通配符字符進(jìn)行轉(zhuǎn)義: 在代碼中檢查 SQL 注入 應(yīng)檢查所有調(diào)用 EXECUTE、EXEC 或 sp_executesql 的代碼??梢允褂妙愃迫缦碌牟樵儊韼椭鷺?biāo)識(shí)包含這些語句的過程。 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() 包裝參數(shù)在選擇的每個(gè)存儲(chǔ)過程中,驗(yàn)證是否對動(dòng)態(tài) Transact-SQL 中使用的所有變量都進(jìn)行了正確處理。來自存儲(chǔ)過程的輸入?yún)?shù)的數(shù)據(jù)或從表中讀取的數(shù)據(jù)應(yīng)包裝在 QUOTENAME() 或 REPLACE() 中。請記住,傳遞給 QUOTENAME() 的 @variable 值的數(shù)據(jù)類型為 sysname,且最大長度為 128 個(gè)字符。
使用此方法時(shí),可對 SET 語句進(jìn)行如下修改: --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'''' 由數(shù)據(jù)截?cái)鄦⒂玫淖⑷?/H3>如果 QUOTENAME() 和 REPLACE() 返回的字符串超過了分配的空間,該字符串將被自動(dòng)截?cái)唷R韵率纠袆?chuàng)建的存儲(chǔ)過程顯示了可能出現(xiàn)的情況。 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 因此,以下語句將把所有用戶的密碼都設(shè)置為在前面的代碼中傳遞的值。 EXEC sp_MyProc '--', 'dummy', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678' 使用 REPLACE() 時(shí),可以通過超出分配的緩沖區(qū)空間來強(qiáng)迫字符串截?cái)?。以下示例中?chuàng)建的存儲(chǔ)過程顯示了可能出現(xiàn)的情況。 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() 一樣,可以通過聲明對所有情況都足夠大的臨時(shí)變量來避免由 REPLACE() 引起的字符串截?cái)唷?yīng)盡可能直接在動(dòng)態(tài) Transact-SQL 內(nèi)調(diào)用 QUOTENAME() 或 REPLACE()?;蛘?,也可以按如下方式計(jì)算所需的緩沖區(qū)大小。對于 @outbuffer = QUOTENAME(@input),@outbuffer 的大小應(yīng)為 2*(len(@input)+1). 。使用 REPLACE() 和雙引號(hào)時(shí)(如上一示例),大小為 2*len(@input) 的緩沖區(qū)便已足夠。 以下計(jì)算涵蓋所有情況: 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, ']') 時(shí)的截?cái)?/H3> |
當(dāng) SQL Server 安全對象的名稱被傳遞給使用 QUOTENAME(@variable, ']') 形式的語句時(shí),可能發(fā)生截?cái)?。以下代碼顯示了這一可能性。
CREATE PROCEDURE sp_MyProc
@schemaname sysname,
@tablename sysname,
AS
-- Declare a variable as sysname. The variable will be 128 characters.
-- But @objectname actually must accommodate 2*258+1 characters.
DECLARE @objectname sysname
SET @objectname = QUOTENAME(@schemaname)+'.'+ QUOTENAME(@tablename)
-- Do some operations.
GO
當(dāng)您串聯(lián) sysname 類型的值時(shí),應(yīng)使用足夠大的臨時(shí)變量來保存每個(gè)值的最多 128 個(gè)字符。應(yīng)盡可能直接在動(dòng)態(tài) Transact-SQL 內(nèi)調(diào)用 QUOTENAME()?;蛘撸部梢园瓷弦徊糠炙鰜碛?jì)算所需的緩沖區(qū)大小。
全部評論6 | |
admin
2011年1月31日 1:8
所謂SQL注入式攻擊,就是攻擊者把SQL命令插入到Web表單的輸入域或頁面請求的查詢字符串,欺騙服務(wù)器執(zhí)行惡意的SQL命令。在某些表單中,用戶輸入的內(nèi)容直接用來構(gòu)造(或者影響)動(dòng)態(tài)SQL命令,或作為存儲(chǔ)過程的輸入?yún)?shù),這類表單特別容易受到SQL注入式攻擊。常見的SQL注入式攻擊過程類如:
?、?某個(gè)ASP.NET Web應(yīng)用有一個(gè)登錄頁面,這個(gè)登錄頁面控制著用戶是否有權(quán)訪問應(yīng)用,它要求用戶輸入一個(gè)名稱和密碼。
?、?登錄頁面中輸入的內(nèi)容將直接用來構(gòu)造動(dòng)態(tài)的SQL命令,或者直接用作存儲(chǔ)過程的參數(shù)。下面是ASP.NET應(yīng)用構(gòu)造查詢的一個(gè)例子:
System.Text.StringBuilder query = new System.Text.StringBuilder("SELECT * from Users WHERE login = ’")。Append(txtLogin.Text)。Append("’ AND password=’")。Append(txtPassword.Text)。Append("’");
?、?攻擊者在用戶名字和密碼輸入框中輸入"’或’1’=’1"之類的內(nèi)容。
?、?用戶輸入的內(nèi)容提交給服務(wù)器之后,服務(wù)器運(yùn)行上面的ASP.NET代碼構(gòu)造出查詢用戶的SQL命令,但由于攻擊者輸入的內(nèi)容非常特殊,所以最后得到的SQL命令變成:SELECT * from Users WHERE login = ’’ or ’1’=’1’ AND password = ’’ or ’1’=’1’.
?、?服務(wù)器執(zhí)行查詢或存儲(chǔ)過程,將用戶輸入的身份信息和服務(wù)器中保存的身份信息進(jìn)行對比。
?、?由于SQL命令實(shí)際上已被注入式攻擊修改,已經(jīng)不能真正驗(yàn)證用戶身份,所以系統(tǒng)會(huì)錯(cuò)誤地授權(quán)給攻擊者。
如果攻擊者知道應(yīng)用會(huì)將表單中輸入的內(nèi)容直接用于驗(yàn)證身份的查詢,他就會(huì)嘗試輸入某些特殊的SQL字符串篡改查詢改變其原來的功能,欺騙系統(tǒng)授予訪問權(quán)限。
系統(tǒng)環(huán)境不同,攻擊者可能造成的損害也不同,這主要由應(yīng)用訪問數(shù)據(jù)庫的安全權(quán)限決定。如果用戶的帳戶具有管理員或其他比較高級(jí)的權(quán)限,攻擊者就可能對數(shù)據(jù)庫的表執(zhí)行各種他想要做的操作,包括添加、刪除或更新數(shù)據(jù),甚至可能直接刪除表。
二、如何防范?
好在要防止ASP.NET應(yīng)用被SQL注入式攻擊闖入并不是一件特別困難的事情,只要在利用表單輸入的內(nèi)容構(gòu)造SQL命令之前,把所有輸入內(nèi)容過濾一番就可以了。過濾輸入內(nèi)容可以按多種方式進(jìn)行。
?、?對于動(dòng)態(tài)構(gòu)造SQL查詢的場合,可以使用下面的技術(shù):
第一:替換單引號(hào),即把所有單獨(dú)出現(xiàn)的單引號(hào)改成兩個(gè)單引號(hào),防止攻擊者修改SQL命令的含義。再來看前面的例子,“SELECT * from Users WHERE login = ’’’ or ’’1’’=’’1’ AND password = ’’’ or ’’1’’=’’1’”顯然會(huì)得到與“SELECT * from Users WHERE login = ’’ or ’1’=’1’ AND password = ’’ or ’1’=’1’”不同的結(jié)果。
第二:刪除用戶輸入內(nèi)容中的所有連字符,防止攻擊者構(gòu)造出類如“SELECT * from Users WHERE login = ’mas’ —— AND password =’’”之類的查詢,因?yàn)檫@類查詢的后半部分已經(jīng)被注釋掉,不再有效,攻擊者只要知道一個(gè)合法的用戶登錄名稱,根本不需要知道用戶的密碼就可以順利獲得訪問權(quán)限。
第三:對于用來執(zhí)行查詢的數(shù)據(jù)庫帳戶,限制其權(quán)限。用不同的用戶帳戶執(zhí)行查詢、插入、更新、刪除操作。由于隔離了不同帳戶可執(zhí)行的操作,因而也就防止了原本用于執(zhí)行SELECT命令的地方卻被用于執(zhí)行INSERT、UPDATE或DELETE命令。
?、?用存儲(chǔ)過程來執(zhí)行所有的查詢。SQL參數(shù)的傳遞方式將防止攻擊者利用單引號(hào)和連字符實(shí)施攻擊。此外,它還使得數(shù)據(jù)庫權(quán)限可以限制到只允許特定的存儲(chǔ)過程執(zhí)行,所有的用戶輸入必須遵從被調(diào)用的存儲(chǔ)過程的安全上下文,這樣就很難再發(fā)生注入式攻擊了。
?、?限制表單或查詢字符串輸入的長度。如果用戶的登錄名字最多只有10個(gè)字符,那么不要認(rèn)可表單中輸入的10個(gè)以上的字符,這將大大增加攻擊者在SQL命令中插入有害代碼的難度。
⑷ 檢查用戶輸入的合法性,確信輸入的內(nèi)容只包含合法的數(shù)據(jù)。數(shù)據(jù)檢查應(yīng)當(dāng)在客戶端和服務(wù)器端都執(zhí)行——之所以要執(zhí)行服務(wù)器端驗(yàn)證,是為了彌補(bǔ)客戶端驗(yàn)證機(jī)制脆弱的安全性。
在客戶端,攻擊者完全有可能獲得網(wǎng)頁的源代碼,修改驗(yàn)證合法性的腳本(或者直接刪除腳本),然后將非法內(nèi)容通過修改后的表單提交給服務(wù)器。因此,要保證驗(yàn)證操作確實(shí)已經(jīng)執(zhí)行,唯一的辦法就是在服務(wù)器端也執(zhí)行驗(yàn)證。你可以使用許多內(nèi)建的驗(yàn)證對象,例如RegularExpressionValidator,它們能夠自動(dòng)生成驗(yàn)證用的客戶端腳本,當(dāng)然你也可以插入服務(wù)器端的方法調(diào)用。如果找不到現(xiàn)成的驗(yàn)證對象,你可以通過CustomValidator自己創(chuàng)建一個(gè)。
⑸ 將用戶登錄名稱、密碼等數(shù)據(jù)加密保存。加密用戶輸入的數(shù)據(jù),然后再將它與數(shù)據(jù)庫中保存的數(shù)據(jù)比較,這相當(dāng)于對用戶輸入
的數(shù)據(jù)進(jìn)行了“消毒”處理,用戶輸入的數(shù)據(jù)不再對數(shù)據(jù)庫有任何特殊的意義,從而也就防止了攻擊者注入SQL命令。 System.Web.Security.FormsAuthentication類有一個(gè) HashPasswordForStoringInConfigFile,非常適合于對輸入數(shù)據(jù)進(jìn)行消毒處理。
?、?檢查提取數(shù)據(jù)的查詢所返回的記錄數(shù)量。如果程序只要求返回一個(gè)記錄,但實(shí)際返回的記錄卻超過一行,那就當(dāng)作出錯(cuò)處理。
該評論在 2011/1/31 1:08:09 編輯過 |
|
admin
2011年1月31日 1:8
sql注入就是,通過語句的連接做一些不是你想要的操作..
舉個(gè)例子你就懂了
例如你要查詢id=1的記錄,直接連接就是這樣"select * from tableName where id=1"
別人可以寫成"select * from tableName where id=1;delete from tableName"
這樣就把你的表數(shù)據(jù)全部刪除了.就是加個(gè);繼續(xù)寫腳本,當(dāng)然,這只是個(gè)例子..還能做其他操作,
比如獲取你數(shù)據(jù)庫的用戶名,密碼什么的,那就慘了,,傳參的方式可以防止注入
"select * from tableName where id=@id" 然后給@id賦值,就ok啦..
這是我的個(gè)人看法,,期待更好的解答
該評論在 2011/1/31 1:08:27 編輯過 |
|
admin
2011年1月31日 1:9 在conn.asp中添加代碼: 該評論在 2011/1/31 1:10:09 編輯過 |
|
admin
2011年1月31日 1:28 防止SQL病毒注入都有那些好方法呢?附加點(diǎn)代碼,謝謝了! 在你接收url參數(shù)的時(shí)候 過濾特殊字符就可以了 veryeasy~~ 該評論在 2011/1/31 1:29:22 編輯過 |
|
admin
2011年1月31日 1:35
在.NET中,如何防止sql注入了? 最好的方法是什么了
首先 你需要在客戶端盡量的限制非法字符 可以用正則表達(dá)式
再次 你需要在sql語句中限制 如果是C#的話 可以再SQL字符串前面加@
最后 盡量使用存儲(chǔ)過程而少用SQL語句
這樣基本就差不多了 再能被注入就是高手了 那個(gè)防不勝防
該評論在 2011/1/31 1:35:05 編輯過 |
|
admin
2011年1月31日 1:39
Chr(0)是什么意思?
后面加chr(0)是標(biāo)準(zhǔn)化格式,也是為了安全,防止類似“注入”之類的讓你的原路徑c:\123被更改而被它人利用,
你可以看一下這三句就知道區(qū)別了
SHFileOp.pFrom = "c:\123" + Chr(0)
(這里的路徑為 c:\123 )
SHFileOp.pFrom = "c:\123" + Chr(0) + Chr(65)
(這里的路徑仍為 c:\123 )
SHFileOp.pFrom = "c:\123" + Chr(65) + Chr(65)
(這里的路徑就為 c:\123AA )
該評論在 2011/1/31 1:39:53 編輯過 |