楼梯到T-SQL:超越基础水平9:动态T-SQL代码

楼梯到T-SQL:超越基础水平9:动态T-SQL代码sql

  做者:Gregory Larsen,2016/07/29(第一次发布:2014/07/23)数据库

本文是楼梯系列的一部分:T-SQL楼梯:超越基础编程

接下来从他的楼梯到T-SQL DML,Gregory Larsen涵盖了更高级的T-SQL语言,如子查询。安全

什么是动态TSQL,为何要使用它?服务器

动态的TSQL是什么?动态TSQL是一种每次你运行它均可能生成并执行生成不一样的TSQL代码,生成的代码是基于某些条件来建立的。在批处理参数中,当“条件或参数”不一样时,TSQL代码会生成不一样的TSQL来执行。你一般使用动态TSQL,当你但愿以编程方式肯定TSQL字数据库表中所须要的,或参数和/或数据时,动态TSQL的用途是无穷无尽的。下面是你可能想要使用动态TSQL的两个示例:测试

一、你想要用管理员身份去从下拉列表中选择一些标准,这可能会使查询的顺序不一样于排列顺序。ui

二、你的应用程序不知道要运行的这个表的名称。编码

 

因为TSQL语言不容许你使用变量或参数特定的表或列名,所以可使用动态TSQL.设计

 

为了更好地理解动态tsql,让咱们来看几个例子。code

 

建立简单的动态数据TSQL

  对于如何建立动态TSQL的第一个示例,让咱们考虑下面的状况。

  假设你有一个应用程序,用户界面容许用户从下拉列表中选择想要读取的表。所以,每当有人使用这个接口时,他们就能够选择不一样的表来返回数据。对于本例,咱们假设这个用户界面显示来自 AdventureWorks2012 的表格信息:数据库,用户选择表中列出的清单中的代码使用动态TSQL代码返回前10条记录。

-- Declare variable to hold dynamic TSQL code DECLARE @CMD nvarchar(1000); -- Declare name of table to read DECLARE @Table nvarchar(125); SET @Table = 'AdventureWorks2012.Sales.SalesOrderDetail'; -- Build dynamic TSQL Statement SET @CMD = 'SELECT TOP 10 * FROM ' + @Table; --Execute dynamic TSQL Statement EXECUTE (@CMD);

 

清单1:简单的动态TSQL示例

 

清单中的代码首先声明一个变量名称@CMD来保存,将要构建的动态选择语句和@Table变量保存表名。而后将表变量设置为

让我来构建个人实际动态

TSQL语句使用SET语句,这个语句设置了变量

对包含选择的链接字符串值的@CMD语句和@Table变量值,而后执行个人动态,使用执行的@CMD变量中包含的TSQL语句声明。

  为了进一步测试清单一中的动态TSQL,能够尝试一下方法,利用AdventureWork2012 的不一样,经过更改“集合”中的代码表@Table,使用语句的语句。

处理更复杂的动态SQL服务器需求

有时你须要编写一些更复杂的动态TSQL。

在这种状况下。可能须要作的是何时想要生成代码来执行某种数据库维护。当须要为数据库维护构建动态的TSQL时,一般会读取system视图,而后生成显示或执行的脚本。假设你是一个数据库管理员,它接管了一个数据库,你想要删除几个测试表在数据库中建立的表中都有之前缀“Test”开头的名称,以演示如何读取系统。表视图和生成适当的DELETE语句,让咱们看看清单2的代码。

-- Section 1: Create database and Sample Tables USE master; go CREATE DATABASE DYNA; GO USE DYNA;  GO CREATE TABLE MyData1 (Id int, DataDesc varchar(100)); CREATE TABLE MyData2 (Id int, DataDesc varchar(100)); CREATE TABLE TestData1 (Id int, DataDesc varchar(100)); CREATE TABLE TestData2 (Id int, DataDesc varchar(100)); GO -- Section 2: Dynamic TSQL code to generate script to delete Test tables USE DYNA; GO DECLARE @TableName varchar(100); DECLARE @CMD varchar(1000); SELECT TOP 1 @TableName = name FROM sys.tables WHERE name like 'Test%' ORDER BY name; WHILE @@ROWCOUNT > 0 BEGIN    SELECT @CMD = 'DROP TABLE ' + @TableName + ';';  PRINT @CMD EXECUTE(@CMD);    SELECT TOP 1 @TableName = name FROM sys.tables  WHERE name like 'Test%' and name > @TableName ORDER BY name; END -- Section 3: Cleanup  USE master; GO DROP DATABASE DYNA;

清单2:删除测试表的动态代码

清单2中的代码包含三个不一样的部分。第一部分建立了一个名为DYNA的数据库,而后建立4个不一样的表,其中两个表以“Test”开头,这两个以“Test”开头的表,表一想用动态TSQL代码删除这些表,代码的第二部分是个人动态TSQL代码,代码的最后一部分经过删除测试数据库来清理。

  若是你查看第二节中的代码,你会发现动态TSQL代码首先打印出它运行的delete语句,而后删除我在第一部分中建立的测试表。我经过处理一个WHILE循环,在寻找不一样的表时,从字符串“Test”开始,我构造了一个储存在变量@CMD中的DELETE命令。而后,我使用PRINT语句来显示DELETE语句,而后使用EXECUTE语句执行语句。最后一部分,经过删除DNYA数据库第三部分来清理。

为了测试这个代码,我建议您从第1节开始,按照顺序独立运行每一个部分。运行第1节后,查看DYNA数据库并验证DYNA数据库中有4个表。 接下来运行第2节。运行此部分时,将在“查询分析器”窗口的“消息”选项卡中看到两条消息。 显示的两个语句是动态生成和执行的两个DELETE语句。 一旦完成了第2节中的代码,请返回并查看DYNA数据库中的表。 若是您在SQL Server Management Studio中使用对象资源管理器,请勿忘记刷新。 或者,您能够从sys.tables视图中进行选择。 如今你应该会发现只有两个表存在,而删除的两个表是那些以“Test”开头的表。 一旦完成验证第2部分中的代码执行后,我将运行第3节中的代码进行清理。 该代码将删除DYNA数据库。

这是一个很是简单的例子,说明如何检查元数据行并生成动态TSQL。 做为DBA,有不少次会派上用场,以了解如何编写生成TSQL代码的TSQL代码。

 

避免SQL注入

 

你可能据说动态TSQL是邪恶的。动态TSQL的恶性部分是开放SQL注入攻击的可能性。 SQL注入是一种黑客技术,恶意用户尝试利用自由格式数据输入字段。这些恶意用户尝试将额外的TSQL代码插入数据输入字段,超出了原始打算使用数据输入字段的方式。经过插入TSQL代码,他们能够愚弄系统返回本来不该该得到的数据,或者更糟的是,对SQL Server数据库运行附加的TSQL命令。根据您的应用程序运行的权限,SQL Injection攻击能够将数据插入到数据库表中,删除表,或更糟糕的是,使用sysadmin权限设置新的登陆。

 

为了演示动态TSQL若是不能正确管理SQL注入攻击,请先用清单3中的代码建立一个数据库和一个表。我将使用该数据库和表来演示动态TSQL如何易受攻击SQL注入攻击。

USE master; go  CREATE DATABASE DYNA;  GO USE DYNA; GO CREATE TABLE Product(ID int,               ProductName varchar(100),                 Price money); INSERT INTO Product VALUES (1, 'Red Wagon', 12.99),                            (2, 'Red Barn', 23.18),                 (2, 'Farm Animals', 7.59),                 (2, 'Toy Solders', 17.76);

清单3:建立数据库和表以演示SQL注入攻击

 

清单3中的代码建立一个数据库名称DYNA,而后建立并填充具备4行数据的表名称Product。

 

假设个人应用程序有一个数据选择屏幕,最终用户能够输入一个包含在ProductName中的文本字符串,而后应用程序将返回包含输入的文本字符串的全部Product表格记录。 应用程序经过将用户输入的文本字符串传递到存储过程名称GetProducts,而后从存储过程返回的数据显示给用户。 存储过程GetProducts的编码如清单4所示。

CREATE PROC GetProducts  (@EnteredText varchar (100)) AS   DECLARE @CMD varchar(1000); SET @CMD = 'SELECT ProductName, Price ' +             'FROM Product ' +            'WHERE ProductName LIKE ''%' +             @EnteredText + '%''';         PRINT @CMD EXEC (@CMD);

 

清单4:存储过程GetUserName的代码

 

经过查看清单4中的存储过程GetProducts,您能够看到此存储过程接受单个参数@EnteredText此参数用于动态建立存储在变量@CMD中的TSQL语句。 而后执行该变量。 (请注意,这个过程多是在不使用动态SQL的状况下编写的。我在这里使用动态SQL来讲明潜在的问题。)

 

为了演示如何使用这个存储过程,我能够经过运行清单5中的代码来执行它。

EXEC GetProducts 'Red';

清单5正常执行存储过程GetUserName

 

清单5中的代码调用GetProducts存储过程,并生成报表1中的结果。

ProductName                                                         Price ------------------------------------------------------------------- ------------Red  Wagon                       12.99 Red Barn 

报告1:使用清单5中的代码调用GetUserName的结果

 

由于个人存储过程GetProducts中的代码使用一个参数并生成varchar变量@CMD,所以存储过程打开以进行SQL注入攻击。 我能够经过使用清单6中的代码执行GetProducts存储过程来演示这一点。

EXEC GetProducts 'Red%'' and ID = 1 --';

清单6:显示GetProducts存储过程如何易受SQL注入的代码

 

若是您查看清单6中的代码,您能够看到我将一些其余字符附加到字符串“Red”到个人存储过程GetProducts。我传递的这些附加字符容许我限制个人查询,只返回ProductName列中具备“Red”的产品,ID值为1.经过容许个人存储过程在@EnteredText参数中使用未编辑的文本,可让我在该参数中注入额外的字符,以使代码执行其余最初未在GetProducts存储过程当中使用的操做。

 

在个人最后一个例子中,我使用myGetProducts存储过程当中的动态TSQL向您展现了非破坏性SQL注入攻击。大多数SQL注入攻击正在尝试从系统中获取额外的数据,或者只是想破坏您的数据库。咱们再来看一下清单7中的代码。

EXEC GetProducts 'Red'' ;SELECT * FROM Product;--';

清单7:SQL注入以返回其余数据

 

若是我运行清单7中的代码,它会生成两个结果集。 第一个结果集具备零行,第二个集合是报表2中的文本:

ID          ProductName              
   Price
----------- ------------------------------
1           Red Wagon          12.99
2           Red Barn          23.18
2           Farm Animals     7.59
2           Toy Solders      17.76

报告2:运行清单7中的代码时的文本结果

若是比较结果1中找到的GetProduct存储过程的正常执行结果与结果2中找到的结果,您能够看到清单7中的代码生成了一些其余输出列,个人存储过程最初没有设计为显示,可是因为SQL注入攻击而显示。

清单7中的示例仍然不是对SQL Injection的破坏性使用,但它容许我利用GetProduct存储过程的@EnteredText参数来返回Client表的全部列的数据。为了完成这个,我添加了“'; SELECT * FROM Product; - ”字符串到个人参数。请注意,在个人附加字符串末尾添加了两个破折号(“ - ”)。这容许我在参数后面注释掉个人存储过程可能包含的任何字符或代码。

对于个人最后一个例子,让我执行一个破坏性的TSQL注入攻击。查看清单8中的代码以查看个人破坏性TSQL注入命令。

EXEC GetProducts 'Red'' ;DROP TABLE Product;--';

清单8:破坏性TSQL注入EXEC命令

 

在清单8中,我向@EMAIL参数添加了一个DELETE语句。 在这个例子中,我删除了客户端表。 若是我运行清单8中的代码,它将删除Client表

 

如何打击SQL注入攻击

 

没有人想要让他们的代码受到SQL注入攻击的危害。为了防止SQL Injection攻击,您应该在开发TSQL应用程序代码时考虑如下几点:

•避免SQL Injection攻击的最佳方法是不使用动态SQL

•编辑用户输入的特殊字符参数,如分号和注释

•只有在须要支持用户输入的数据时,才能使参数发生

•若是必须使用动态SQL,则使用使用sp_execute sql的参数化TSQL来执行动态TSQL而不是EXEC。

•增强安全性,只容许执行动态TSQL所需的最少权限。

 

若是您的应用规范要求您须要构建一些包含动态TSQL的代码,那么使用参数化的TSQL是打击SQL注入的好方法。在清单9中,我提供了一个我如何修改个人GetUserName存储过程以使用参数化的TSQL的例子。

ALTER PROC GetProducts   (@EnteredText varchar (100)) AS   DECLARE @CMD nvarchar(1000); DECLARE @WildCardParm varchar(102); SET @CMD = 'SELECT ProductName, Price ' +             'FROM Product ' +            'WHERE ProductName LIKE @EnteredParm'; SET @WildCardParm = '%' + @EnteredText + '%'; EXEC sp_executesql @CMD,N'@EnteredParm varchar(100)',@EnteredParm=@WildCardParm;

 

清单9:使用参数化的TSQL

 

在清单9中,我更改了个人GetProducts存储过程,以使用sp_executesql来执行个人动态TSQL。在这个修改后的存储过程当中,我作了如下更改:

•将字符串@CMD更改成再也不包含命令字符串中的@EnteredText变量的值。而是将用户输入的文本引入名为@EnteredParm的变量中。

•添加了一个SET语句来设置变量@WildCardParm将通配符(%)放在@EnteredText参数的开头和结尾。

•更改了如何执行字符串@CMD。而不是使用EXEC语句来执行字符串,我使用过程sp_executesql。

 

经过进行这两个更改,用户输入的文本如今将做为参数驱动查询执行。经过这样作,用户不能再尝试在个人GetProduct存储过程当中注入额外的TSQL代码。要验证这一点,请运行清单5,6,7和8所示的四个不一样的命令。可是因为我已经删除了个人产品表,因此我首先须要用数据从新建立它。为此,我须要首先运行清单9中的代码。

CREATE TABLE Product(ID int,                  ProductName varchar(100),                 Price money); INSERT INTO Product VALUES (1, 'Red Wagon', 12.99),                            (2, 'Red Barn', 23.18),                  (2, 'Farm Animals', 7.59),                (2, 'Toy Solders', 17.76);

清单9:建立并填充Client

 

在运行清单9以从新建立个人产品表以后,我能够运行列表5,6,7和8来证实我解决了个人SQL注入问题。 当您运行这些不一样的命令时,您将发现只有清单5返回数据。 其余人不返回数据的缘由是如今生成的动态TSQL正在寻找包含其余用户输入注释值的ProductName值,固然这与“Product”表中的任何Product列值不匹配。

 

概要

 

没有人想要对他们的手表进行SQL注入攻击。 固然,确保不会发生的最佳解决方案是在您的应用程序中没有动态SQL代码。 若是您的应用程序确实须要动态SQL,那么本文将为您提供一些有关如何最小化与SQL注入相关的风险的建议。 下次写动态SQL时,请确保采起措施避免SQL注入攻击的可能性。

 

问题和答案

 

在本节中,您能够经过回答下列问题来回顾您对SQL注入的了解程度。

 

问题1

 

避免SQL注入攻击的最佳方法是什么(最好的方法)?

•不要部署使用动态TSQL的TSQL代码

•编辑用户输入的动态TSQL中用于容许SQL注入攻击的特殊字符的数据

•使用户输入动态TSQL的参数尽量短

•使用参数化的TSQL代码

问题2

 

用户可使用SQL注入附件来完成哪些事情(选择全部适用的内容)?

•返回应用程序无心为用户选择的数据

•将数据插入到应用程序不打算使用的表中

•放一张桌子

•为新账户提供系统管理员权限

• 以上全部

 

问题3

 

若是要部署变量中包含的动态TSQL代码,最好使用这两种执行方法中的哪种来最大程度下降SQL注入攻击的风险?

•执行

•sp_executesql

 

回答:

 

问题1

 

正确的答案是避免SQL注入的最佳方法是不容许您的应用程序中的动态TSQL代码。 。

 

问题2

 

正确的答案是e,以上全部。使用SQL Injection,恶意用户能够执行许多不一样的SQL操做。它们能够执行的命令类型取决于用于运行动态TSQL命令的账户的权限。若是应用程序账户具备sysadmin权限,则SQL Injection攻击能够执行用户想要的任何操做。

 

问题3

 

正确的答案是b。经过使用sp_executesql,您能够传递用户使用参数输入数据到参数化的TSQL代码。

本文是T-SQL楼梯的一部分:超越基础楼梯

相关文章
相关标签/搜索