首页 > 开发 > 综合 > 正文

如此高效通用的分页存储过程是带有sql注入漏洞的

2024-07-21 02:11:45
字体:
来源:转载
供稿:网友
google中搜索“分页存储过程”会出来好多结果,是大家常用的分页存储过程,今天我却要说它是有漏洞的,而且漏洞无法通过修改存储过程进行补救,如果你觉得我错了,请读下去也许你会改变看法。
通常大家都会认为存储过程可以避免sql注入的漏洞,这适用于一般的存储过程,而对于通用分页存储过程是不适合的,请看下面的代码和分析!
 
一般的通用的分页存储过程代码如下:
 
通用分页存储过程
create procedure pagination
@tblname varchar(255), -- 表名 
@strgetfields varchar(1000) = '*', -- 需要返回的列 
@fldname varchar(255)='', -- 排序的字段名 
@pagesize int = 10, -- 页尺寸 
@pageindex int = 1, -- 页码 
@docount bit = 0, -- 返回记录总数, 非 0 值则返回 
@ordertype bit = 0, -- 设置排序类型, 非 0 值则降序 
@strwhere varchar(1500) = '' -- 查询条件 (注意: 不要加 where) 
as 

declare @strsql varchar(5000) -- 主语句 
declare @strtmp varchar(110) -- 临时变量 
declare @strorder varchar(400) -- 排序类型 
if @docount != 0 
begin 
if @strwhere !='' 
set @strsql = 'select count(*) as total from [' + @tblname + '] where '[email protected] 
else 
set @strsql = 'select count(*) as total from [' + @tblname + ']' 
end 
--以上代码的意思是如果@docount传递过来的不是0,就执行总数统计。以下的所有代码都是@docount为0的情况 

else 
begin 
if @ordertype != 0 
begin 
set @strtmp = '<(select min' 
set @strorder = ' order by [' + @fldname +'] desc' 
--如果@ordertype不是0,就执行降序,这句很重要! 
end 

else 
begin 
set @strtmp = '>(select max' 
set @strorder = ' order by [' + @fldname +'] asc' 
end 
if @pageindex = 1 
begin 
if @strwhere != '' 
set @strsql = 'select top ' + str(@pagesize) +' '[email protected]+ ' from [' + @tblname + '] where ' + @strwhere + ' ' + @strorder 
else 
set @strsql = 'select top ' + str(@pagesize) +' '[email protected]+ ' from ['+ @tblname + '] '+ @strorder 
--如果是第一页就执行以上代码,这样会加快执行速度 
end 

else 
begin 
--以下代码赋予了@strsql以真正执行的sql代码 
set @strsql = 'select top ' + str(@pagesize) +' '[email protected]+ ' from [' 
+ @tblname + '] where [' + @fldname + ']' + @strtmp + '(['+ @fldname + ']) from (select top ' + str((@pageindex-1)*@pagesize) + ' ['+ @fldname + '] from [' + @tblname + ']' + @strorder + ') as tbltmp)'+ @strorder 
if @strwhere != '' 
set @strsql = 'select top ' + str(@pagesize) +' '[email protected]+ ' from [' 
+ @tblname + '] where [' + @fldname + ']' + @strtmp + '([' 
+ @fldname + ']) from (select top ' + str((@pageindex-1)*@pagesize) + ' [' 
+ @fldname + '] from [' + @tblname + '] where ' + @strwhere + ' ' 
+ @strorder + ') as tbltmp) and ' + @strwhere + ' ' + @strorder 
end 
end 
exec (@strsql)
go

 
大家可以看到上面的存储过程中是通过一些步骤最终拼接成一个sql字符串,然后通过exec执行这个串得到分页的结果。
 
我们假定要做一个这样的查询,通过用户名username模糊查询用户,为了叙述方便,便于理解我们只考虑取第一页的情况,取出存储过程中取第一页的拼串行如下:
set @strsql = 'select top ' + str(@pagesize) + ' ' + @strgetfields + ' from [' + @tblname + ']  where ' + @strwhere + ' ' + @strorder
 
为了便于说明问题,我们可以假定@pagesize20@strgetfields ‘*’@tblnameuseraccount,@strorder’ order  by  id desc’ 那么上面一行可以写成如下形式:
set @strsql = 'select top 20 *  from [useraccount]  where ' + @strwhere  + ' order by id desc’
 
我们可以假定用户输入的模糊用户名是: jim’s dog
我们用sqlparameter传递参数给分页存储过程@strwhere 的值是:’username like ‘’%jim’’ dog%’’’(注意like后边的字符串中的单引号已经全部变成两个单引号了),我们将这个值代入上面的@strsql赋值语句中,如下:
 
set @strsql = 'select top 20 *  from [useraccount]  where  username like ''%jim'' dog%'' order by id desc’
 
让我们写上声明变量的部分执行在查询分析器中测试一下,代码如下:
 
declare @strsql varchar(8000)
declare @strwhere varchar(1000)
set @strwhere = 'username like ''%jim'' dog%'''
set @strsql = 'select top 20 *  from [useraccount]  where ' + @strwhere + ' order by id desc'
print @strsql
exec (@strsql)

大家可以把上面几行代码粘贴到查询分析器中执行一下,就可以看到下面的画面:


在消息的第一行,打印出了要执行的sql语句,很显然此语句的 like ‘%jim’ 后面的部分全部被截断了,也就是说如果用户输入的不是jim’s dog而是jim’ delete from useraccount那么会正确的执行删除操作,传说中的sql注入就会出现了。
 
问题出现了,我们应该怎么解决问题?
1.  很显然我们使用sqlparameter传递参数已经将单引号替换成了连个单引号了,可是因为我们在数据库中拼串导致替换并不能解决问题。
2.  根据我的实验证明如果使用存储过程不可能解决这个问题,我们只有将这个存储过程要执行的拼串操作放到数据访问层去做,才可以避免这个问题。
 
如果大家有在存储过程中解决这个问题的办法,请不吝赐教。
备注:本文说的是ms sql server2000 的数据库,而非使用sql 2005的新特性分页。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表