1. 程式人生 > >翻譯(三)Stairway to T-SQL: Beyond The Basics Level 9: Dynamic T-SQL Code

翻譯(三)Stairway to T-SQL: Beyond The Basics Level 9: Dynamic T-SQL Code

數據庫表 studio 應用程序 cmd char 結束 管理 分代 應對

Stairway to T-SQL: Beyond The Basics Level 9: Dynamic T-SQL Code

By Gregory Larsen, 2016/07/29 (first published: 2014/07/23)

原文鏈接:http://www.sqlservercentral.com/articles/Stairway+Series/113118/

The Series

本文是系列的一部分:通往T-SQL的階梯:超越基礎

下面就從它通往T-SQL DML的階梯、Gregory Larsen所敘述的覆蓋更先進的T-SQL語言方面如子查詢。

還有當你需要寫代碼,創建特定的TSQL代碼並執行它的時候。當你這樣做時你正在創建動態TSQL代碼。你用來創建動態TSQL可能是簡單的代碼,也可以是復雜的。當寫動態TSQL你需要了解動態代碼打開一個SQL註入攻擊的可能性。在這篇文章中,我解釋了為什麽要使用動態TSQL如何生成動態TSQL。我還將探討SQL註入和討論如何避免在你的動態TSQL代碼SQL註入式攻擊。

什麽是動態TSQL和你為什麽要使用它?

動態TSQL究竟是什麽?動態TSQL是可能是不同的代碼,每次你運行它。這是一批TSQL代碼生成和執行。基於批處理中的某些條件或參數創建了即時生成的代碼。當“條件或參數不同的TSQL代碼產生不同的TSQL來執行。

你通常使用動態TSQL當你想以編程方式確定什麽TSQL你需要基於數據庫中的表的參數和/或數據。動態TSQL的用途是無止境的。這裏有兩個你可能想要使用動態TSQL的例子:

您希望用戶從下拉列表中選擇一些可能導致查詢運行不同的標準,如排序順序。

應用程序不知道要運行的表的名稱,直到運行時為止。

由於TSQL語言不允許你使用變量或參數的特定表或列的名稱,可以用來代替動態TSQL。

為了更好地了解動態TSQL讓我們看幾個例子。

創建簡單動態TSQL

對於第一個例子,如何創建動態TSQL讓我們考慮下面的情況。假設您有一個應用程序,其中用戶界面允許用戶從下拉列表中選擇要讀取的表。因此,每次有人使用接口時,他們都可以選擇一個不同的表,從中返回數據。在這個例子中我們假設這個用戶界面顯示的表的信息從adventureworks2012數據庫和用戶選擇adventureworks2012.sales.salesorderdetail表。清單1中的代碼顯示了一個使用動態TSQL代碼從adventureworks.sales.salesorderdetail表返回的前10 條記錄的方法。

技術分享

清單1:簡單的動態TSQL實例

清單1中的代碼首先聲明一個變量名稱@CDM來保存要構建的動態SELECT語句和@表變量來保存表名。然後我為表adventureworks.sales.salesorderdetail設置表變量。建立我的實際動態TSQL語句我可以使用SET語句。此語句將變量CMD設置為包含SELECT語句和@表變量值的級聯字符串值。然後我執行我的包含在@CDM變量中的動態TSQL語句使用EXECUTE語句執行。

為了進一步檢驗清單1中的動態TSQL,你可以嘗試使用不同的代碼通過改變adventurework2012表的“設置”表=“來聲明使用這個表。

處理更復雜的動態SQL Server需求

還有當你需要寫一些更復雜的動態TSQL倍。作為DBA,我可能需要這樣做的一種情況是,當我想生成代碼來執行某種數據庫維護時。當我需要建立數據庫維護的目的,我通常看一個系統視圖和生成腳本,顯示和/或執行動態TSQL。假設您是一個DBA,接管了維護一個數據庫,並希望刪除在數據庫中創建的幾個測試表。表中都有以前綴“test”開頭的名稱。表明你可能讀sys.tables視圖並生成相應的delete語句,讓我們看清單2中的代碼。

技術分享

清單2:動態代碼刪除測試表

清單2中的代碼包含三個不同的部分。第一部分創建的數據庫稱為DYNA,然後創建4個不同的表,其中兩張表以“TEST”開頭從,這兩張表以“TEST”開頭是我想刪除動態TSQL代碼。代碼的第二部分是我的動態TSQL代碼。最後一部分代碼通過刪除我創建的測試數據庫進行清理。

如果你查看代碼,在2節中你會發現動態TSQL代碼首先打印出DELETE語句,它運行,然後刪除我在1節中創建的測試表。我通過while循環處理,同時查找以字符串“test”開頭的不同的表。對於每一個表,我發現以“test”開頭,我構建了一個刪除命令,該命令存儲在變量CMD中。然後,通過使用打印語句顯示刪除語句,然後緊接著通過使用執行語句執行語句。最後一部分,第三部分清理刪除DYNA數據庫。

為了測試此代碼,我建議您從第1節開始獨立運行每個部分。當您運行1節回顧動態數據庫和驗證在動態數據庫表四。下一步,第2節,運行此部分時,您將看到兩個消息顯示在“查詢分析器”窗口中的“消息”選項卡中。顯示的兩個語句是動態生成和執行的兩個刪除語句。一旦你完成了在2節中運行代碼,回去復習你的動態數據庫的表。如果您正在使用SQL Server Management Studio中的對象資源管理器,不要忘記刷新。或者,你可以從sys.tables視圖選擇。您現在應該發現只有兩個表存在,兩個表是以“test”開始的。一旦您完成了第2節中代碼的驗證,我將在第3節中運行代碼以清除。此代碼將刪除DYNA數據庫。

這是一個很簡單的例子關於如何檢查一行數據生成動態TSQL。作為一個DBA,很多時候,它會派上用場,了解如何編寫TSQL代碼生成TSQL代碼。

避免SQL註入

你可能已經聽說了動態TSQL是邪惡的。動態TSQL邪惡的部分是它開辟了SQL註入式攻擊可能性。SQL註入是一種黑客技術,惡意用戶試圖利用自由向數據表輸入字段。這些惡意用戶嘗試插入額外的TSQL代碼到數據輸入字段的數據輸入字段超越最初用意。通過插入TSQL代碼就可以騙過系統返回的數據,得到他們本來不應該得到的,或更糟的是運行額外的TSQL命令對SQL Server數據庫得到應用程序運行的權限,SQL註入攻擊可以將數據插入到數據庫表,刪除表,或者更糟的是安裝一個新的登錄獲得系統管理員權限。

為了表明動態TSQL如果不妥善管理如何不會受到SQL註入攻擊,讓我先創建一個數據庫和清單3中的代碼表。我將使用這個數據庫和表來演示動態TSQL如何容易受到SQL註入攻擊。

技術分享

清單3:創建數據庫和表來演示SQL註入攻擊

清單3中的代碼創建一個叫DYNA的數據庫,然後創建並填充表的4行數據。

假設我的應用程序數據選擇讓用戶向屏幕輸入一個包含在ProductName,然後應用程序將返回所有已有表記錄的包含輸入的文本字符串的文本字符串。該應用程序通過傳遞文本字符串,用戶進入到一個叫getProducts存儲的過程,然後將數據從存儲過程返回顯示給用戶。存儲過程getProducts編碼,如清單4所示。

技術分享

清單4:存儲過程返回用戶名密碼

通過對存儲過程getProducts在清單4中可以看到該存儲過程接受一個參數“enteredtext,這個參數是用來創建一個動態的TSQL語句存儲在變量中的命令。然後執行該變量。(註意,這個過程可能是在沒有使用動態SQL的情況下編寫的,我在這裏使用動態SQL來說明潛在的問題。)

為了演示如何使用這個存儲過程,讓我運行清單5中的代碼來執行它。

技術分享

清單5執行存儲過程返回用戶名通常

清單5中的代碼調用getProducts存儲過程產生結果在報告1中顯示。

技術分享

報告1:從使用清單5中的代碼調用GetUserName結果

因為在我的存儲過程的代碼getProducts需要一個參數生成varchar變量命令它離開存儲過程並且開始SQL註入攻擊。我可以證明這個通過執行getProducts存儲過程與清單6中的代碼。

技術分享

清單6:代碼揭露getProducts存儲過程很容易受到SQL註入

如果你回顧清單6中的代碼,你可以看到我通過了附加一些其他字符的字符串“red”到我的存儲過程getProducts。這些額外的字符通過我的限制查詢只返回的產品有“red”在產品名稱列有1的ID值。讓我的存儲過程enteredtext參數使用未經編輯的文字讓我註入額外的字符轉換為參數來執行其他原本不打算用於getProducts代碼的存儲過程的動作。

在我的最後一個例子中我會給你一個無損的SQL註入攻擊利用動態的TSQL在我的myGetProducts存儲過程。大多數SQL註入攻擊都試圖從系統中獲取額外數據,或者只想破壞數據庫。為了更深入地探討這個問題,讓我們看看清單7中的代碼。

技術分享

清單7:SQL註入以返回其他數據

 
如果我運行清單7中的代碼,它將生成兩個結果集。第一個結果集具有零行,第二組是報告2中找到的文本:

技術分享

報表2:運行清單7中的代碼時的文本結果

 
如果你比較正常的getProduct存儲過程執行結果的的結果1,結果發現2的結果,你可以看到清單7中的代碼生成一些額外的輸出列,我的存儲過程不是最初設計的顯示而是由於SQL註入攻擊。
我在清單7中的例子仍然不是一個破壞性使用SQL註入,但它確實讓我利用@ enteredtext參數的存儲過程的所有列的客戶表返回數據。為了實現這一點,我從產品中添加了“*”;select*from  product。註意,在附加字符串的結尾添加了兩個破折號(“-”)。這允許我註釋出存儲過程在參數之後可能包含的字符或代碼。
 
我的最後一個例子讓我展示一個破壞性的TSQL註入攻擊。在清單8中看到我的破壞性的TSQL註射命令。    

技術分享

清單8:破壞TSQL註射執行命令

在清單8中,我添加了一個刪除語句到@電子郵件參數。在這個例子中,我刪除了客戶表。如果我運行清單8中的代碼,它將刪除客戶表。

如何對付SQL註入攻擊

 
沒有人想要他們的代碼受到SQL註入攻擊受損。為了應對SQL註入攻擊,你應該考慮以下幾點時在開發你的TSQL代碼應用:
為了避免SQL註入攻擊是不使用動態SQL的最好方式
編輯用戶輸入參數的特殊字符,如分號和評論
讓你的參數,只要需要支持用戶輸入的數據
如果你必須使用動態SQL,然後使用參數化SQL執行TSQL使用sp_execute執行動態TSQL,而不是執行。
加強安全性只允許最小的權利需要執行動態TSQL。
如果你的應用程序需要建立一些代碼,包含動態TSQL然後使用參數化TSQL是反SQL註入的一種好方法。在清單9中,我已經提供了我如何修改我的GetUserName存儲過程使用參數化的TSQL實例。
 

技術分享

清單9:使用參數化TSQL

在清單9中,我改變了我的getProducts存儲過程使用sp_executesql執行我的動態TSQL。在這個修改後的存儲過程中,我做了如下更改:
改變字符串@ CMD中不再包括enteredtext變量在命令字符串的值。相反我介紹了用戶向文本輸入一個名為@ EnteredParm。
增加了一個SET語句來設置變量@ WildCardParm在開始增加通配符(%)和在結束增加enteredtext參數。
更改了如何執行字符串CMD。而不是使用exec語句執行字符串,我使用了程序sp_executesql。
通過進行這兩個更改,用戶輸入的文本現在將作為參數驅動的查詢執行。這樣,用戶不可以再試圖進入我的getProduct存儲過程註入更多的TSQL代碼。為了驗證這一點,運行清單5, 6, 7和清單8所示的四個不同的命令。但是因為我已經刪除了我的產品表,我首先需要用數據重新創建它。為此我需要清單9首先運行代碼。
 

技術分享

清單9:創建和填充客戶表

在運行清單9重新創建產品表之後,我可以運行清單5, 6, 7和8,以證明我解決了SQL註入攻擊的問題。運行這些不同的命令時,您會發現只有清單5返回數據。別人不返回數據的原因是動態TSQL產生現在正在尋找包含額外的用戶輸入值ProductName值註入,這當然不符合任何產品的產品表中的列值。
 

總結

沒有人希望看到一個SQL註入攻擊。當然,確保它不發生的最佳解決方案是在應用程序中不使用動態SQL代碼。如果您的應用程序確實需要動態SQL,本文將向您介紹如何盡量減少與SQL註入相關的風險。下一次編寫動態SQL時,一定要采取步驟避免SQL註入攻擊的可能性。
 

問題和答案

在本節中,您可以通過回答以下問題來了解您對SQL註入的理解程度。
 
問題1:
 
避免SQL註入攻擊最好的方法是什麽(最好的方法)?
 
A不部署TSQL代碼使用動態TSQL
B編輯用戶輸入用於特殊字符允許SQL註入攻擊的動態TSQL數據
C讓用戶輸入參數的動態TSQL盡可能短
D使用參數化的TSQL代碼
 
問題2:
 
用戶可以通過SQL註入來完成什麽樣的事情(選擇所有的應用程序)?
 
A返回應用程序不打算讓用戶選擇的數據
B將數據插入到應用程序不打算使用的表中
C刪除表
D系統管理員權限來提供新的帳戶
E以上所有
問題3:
如果你要部署動態TSQL代碼是包含在一個變量,這兩種執行方法最好是用以減少您的風險,SQL註入攻擊?
exec
sp_executesql
答案:
問題1:
正確答案是A.避免SQL註入的最好方式是不允許動態TSQL代碼在您的應用程序。
問題2:
正確答案是E,以上所有。使用SQL註入,惡意用戶可以執行許多不同的SQL操作。這樣的命令可以執行取決於用於運行動態TSQL命令該帳戶的權利。如果應用程序的帳戶具有管理員權限,SQL註入攻擊可以用戶想要做什麽。
問題3:
正確答案是B.利用sp_executesql你可以通過你的用戶輸入數據使用的參數到你的參數化TSQL代碼。
本文是部分對通往T-SQL樓梯的說明:超越了基本的知識





翻譯(三)Stairway to T-SQL: Beyond The Basics Level 9: Dynamic T-SQL Code