一般说来在代码中拼接SQL命令,容易让别人钻空子玩SQL注入,
我看了有些存储过程的代码,发现在存储过程中也是对各参数进行拼接。
这好象与在代码中拼接没什么区别啊,难道在存储过程拼接就更安全?
譬如下面这个存储过程:Create PROCEDURE [dbo].[ShowPage]
@tblName varchar(255), -- 表名
@strGetFields varchar(1000) = ''*'', -- 需要返回的列
@strOrder varchar(255)='''', -- 排序的字段名
@PageSize int = 10, -- 页尺寸
@PageIndex int = 1, -- 页码
@strWhere varchar(1500) = '''' -- 查询条件 (注意: 不要加 where)
AS
declare @strSQL varchar(5000)if @strWhere !=''''
set @strWhere='' where ''+@strWhereset @strSQL=''Select * FROM (Select ROW_NUMBER() OVER (''+@strOrder+'') AS pos,''+@strGetFields+'' FROM [''+@tblName+'']''+@strWhere+'') AS sp Where pos BETWEEN ''+str((@PageIndex-1)*@PageSize+1)+'' AND ''+str(@PageIndex*@PageSize)
本文转摘自『蓝派网』http://www.lan27.com/Article/200810/8986.htm
我看了有些存储过程的代码,发现在存储过程中也是对各参数进行拼接。
这好象与在代码中拼接没什么区别啊,难道在存储过程拼接就更安全?
譬如下面这个存储过程:Create PROCEDURE [dbo].[ShowPage]
@tblName varchar(255), -- 表名
@strGetFields varchar(1000) = ''*'', -- 需要返回的列
@strOrder varchar(255)='''', -- 排序的字段名
@PageSize int = 10, -- 页尺寸
@PageIndex int = 1, -- 页码
@strWhere varchar(1500) = '''' -- 查询条件 (注意: 不要加 where)
AS
declare @strSQL varchar(5000)if @strWhere !=''''
set @strWhere='' where ''+@strWhereset @strSQL=''Select * FROM (Select ROW_NUMBER() OVER (''+@strOrder+'') AS pos,''+@strGetFields+'' FROM [''+@tblName+'']''+@strWhere+'') AS sp Where pos BETWEEN ''+str((@PageIndex-1)*@PageSize+1)+'' AND ''+str(@PageIndex*@PageSize)
本文转摘自『蓝派网』http://www.lan27.com/Article/200810/8986.htm
参数+存储一样是拼接SQL,就其安全性而言,这与在代码中拼接SQL命令字符串 区别在哪里呢?最终得到的SQL命令字符串是一样的.
string xxx="a' or 1=1'";
sql="select * from table where name='"+xxx;
实际上sql得到提select * from table where name='a' or 1=1如果我们用参数+存储的形式,将变量xxx作为参数@p1 传入存储
存储过程中,最终得到SQL命令是:
sql=sql="select * from table where name='"+[@p1]+"'";
这时SQL和在代码中拼接难道不是一样的吗?
最终存储执行 exec (select * from table where name='a' or 1=1)
如果 参数@p1="a' or 1=1"
set @sql="select * from table where name='"+[@p1]+"'" exec(@sql) 难道不等效于
exec (select * from table where name='a' or 1=1) 吗?
new SqlParameter("@Category",SqlDbType.Int)
这样的话这个参数接收的参数就只能是整型了,就能有效的防止部分sql注入了,比如@p1="a' or 1=1" 这样。但不是说所有的存储过程都能防止sql注入。别以为所有查询都是用存储过程写的你的程序就安全了,下面给你看个例子create table tb1(TName varchar(50),id int)
go
insert into tb1 select 'tom',1 union all select 'jone',2create proc getByIds
@Ids varchar(200)
as
begin
declare @sql varchar(max)
set @sql='select * from tb1 where id in('+@Ids+')'
execute(@sql)
end
USE [t1]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[getByIds]
@Ids = N'1,2'
SELECT 'Return Value' = @return_value
GO
--结果
--tom 1
--jone 2--看下面这个参数
USE [t1]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[getByIds]
@Ids = N'1,2) union all select [name],object_id from sys.objects where type=''u''--)'
SELECT 'Return Value' = @return_value
GO
--tom 1
--jone 2
--t3 5575058
--nums 405576483
--TTime 421576540
--tb 437576597
--tb1 453576654
--哦呵,所有的表都暴露出来了,再通过object_id找其它对象,最后找到你数据库的密码也是轻易而举了。drop table tb1
如果真是这样,那么存储+参数的意义仅在于"判断实际输入的数据类型是否与指定的类型相符"这一点点了?
既然只有这点作用,那么也可以通过代码给变量指定相关的数据类型,岂不是达到了同样的目的
譬如 int i;
i=int.prase(xxx.text);
问题是这么多人说存储+参数具有很强大的防SQL注入特点(微软的MSDN也经常这样提),我想不应该只是在于"数据类型的判断"吧.现在真是矛盾吧.感觉很多资料都是互相矛盾,包括微软自己的说法都让人云里雾里,
譬如SQL的参考文档中说:
"SQL Server 中的 Parameters 集合提供了类型检查和长度验证。如果使用 Parameters 集合,则输入将被视为文字值而不是可执行代码"
如果真是这样.楼上朋友的Ids = N'1,2) union all select [name],object_id from sys.objects where type=''u''--)' 将完全被视为一段非执行的字符串而不会执行.按楼上朋友的说法,实际情况又不是这样.
言外之意,存储过程中的代码,还有“不用拼接的另一种写法”?如果有,应该怎么写,高手能不能就
select id from table where name=[@p1]
给一个简单的例子?
都是拼接。只不过一个在程序里面拼接、一个是在数据库中
不过最好的是在提交前,将所有危险字符过滤掉。譬如: exec drop insert 等等类似的当然这样可能会有些弊端
2,没有提高效率,虽然说是存储过程,但是只是编译了拼语句的代码,拼出的语句是动态的,每次不一样,在执行中编译和选择计划的
3,仅有的好处,只能说:
a 一个封装
b 当拼的语句够长时,代码主体如果是存储过程,减少了语句发送到sqlserver的数据量
能不能用一个简单的例子说明一下,譬如 查询某表中id为xx的记录
上面手误打错了。
用法可见联机丛书。如果你在C#中写一段传参的方式执行语句的,在执行的同事,打开sqlserver事件探查器,就会看到,这种执参的不是拼语句而是转为sp_executesql执行。
'select * from tb where name=''' + @name + ''''@tb,@name都是存放的字串,但在拼出的语句里 @name是做值用,@tb是做表对象用。
后者都可以改为sp_executesql执行。
前者不能,即便改用sp_executesql了,也是伪装的,只是形似. sqlserver还不是对象型数据库,没有什么变量可以是一个引用的概念。
直接写成select * from tb where name=@name (这里不加单引号可行么)
另外不要写成下面这种形式:
set sql="select * from tb where name='" + @name + "'"
exec (sql)
而是直接在存储代码中写:
begin
select * from tb where name=@name //即不用exec(sql)形式,同时也没给参数加单引号
end
另外还有人说将exec(sql)换成
exec sp_executesql sql这种形式.不知上面这些说法有无道理
还有必要用exec或sp_executesql吗?
我不赞同第二点,虽然是拼接的sql但是整个执行计划同样是被缓存了的,你可以在sys.dm_exec_query_plan中查询得到
这两者是完全有区别的,exec仅仅只是执行这个即席查询,那么他要经历整个过程 编译->algebrizer->运算符平展->名词解析->类型派生 再到优化再生成执行计划这个过程
而sp_executesql则不然,而是直接调用执行计划,也就跳过了优化阶段,你的意思就是说参数不同生产的执行计划是不同的,这个观点我不赞同的,执行计划是是跟表的结构,链接顺序,是否使用索引,是否并行处理等相关的,虽然参数的值不同,但是整个查询的结构是相同的
对于你第一个回贴的反驳我可以理解。对于这个就完全就不能明白你在反驳我的什么,呵呵declare @name varchar(100)
set @name ='aaa'
即然能写成
select x from tb where name =@name
我又何必去exec或者sp_executesql N'select x from tb where name=@name',N'......
这样呢?
我有说过exec 和sp_executesql是一样的吗?
先不说你是在反驳什么, 对于你描述的后面半句:
执行计划是是跟表的结构,链接顺序,是否使用索引,是否并行处理等相关的,虽然参数的值不同,但是整个查询的结构是相同的除了你说的这些,表中数据的分布很影响查询计划,不同的参数值都可能引发计划的变更.