以前和大家一样,听人说用存储过程+参数能防止SQL注入,听的多了,也就深信不疑,
就算不是绝对的,至少也有很强大的防御力吧。很多人说自从用了参数+存储,不必过滤字符了,腰也不痛了。今天仔细想了想,在“代码中用变量拼接组合SQL命令”的确危险,有漏洞。
但“存储过程+参数”实质不是一样的吗,虽然传递过程两者有区别,一个是传递组合好的SQL命令字符串,
一个是传递 "存储过程名+参数",不过到了数据库,在存储过程的代码中,最终不是还要将这些参数组合拼接成
一段SQL命令吗?
就好比一个炸弹,一种方法是直接把成品运到某处,另一个是把炸弹的各零件运到某处,某处再拼合,最终还是炸弹譬如:
string myname="a' or 1=1";
在代码中拼接SQL命令:
mysql="select * from table where name='"+myname+"'";
即mysql= "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(@sql)难道不等效于 exec (select * from table where name='a' or 1=1) 吗? 
如果是,那和在代码中组合SQL命令有何区别?搜了一下资料,说参数+存储安全性高的大有人在,但认为参数+存储在安全性上并没多大意义的人也不少
,他们认为仍需要进行严格的字符过滤。大家上网搜一下就能看到了。实在是很困惑,请大家各持己见。谢谢。尽量不要用“可以,因为参数是传值”或"你不了解参数的意义"等空洞的回答,再次感谢。

解决方案 »

  1.   

    据说,SqlParameters这个类在处理参数的时候能自动帮你处理~
    据说,使用参数是MSDN推荐的.
      

  2.   

    LZ这种钻研的精神还是值得敬佩的,但讨论这样的问题没什么意义,如果怀疑,你可以去试验一下就知道了。如果试验下来发现有问题,完全可以去向Microsoft报告BUG了。
      

  3.   

    这可不是我一个人的疑惑
    http://topic.csdn.net/t/20041220/19/3661597.html
    http://www.sudu.cn/info/html/edu/20070526/147386.htmlhttp://k.pconline.com.cn/question/162381.html
    http://www.qnr.cn/pc/dj/sanji/shuju/fuxi/200901/115755.html不管怎么样,asp.net代码中给定的SQL的查询或其它操作,最终需要交给SQL数据库内部来处理吧。
    就以我贴子中的列子来讲:
    如果在代码页中,得到变量str="a' or 1=1"
    将变量Str通过SqlParameters 的@p1参数传给SQL服务器,SQL收到的@p1参数值还是不是"a' or 1=1"?
    如果是,那么存储过程最终将执行
    @sql="select * from table where name='"+[@p1]+"'"
    上面这句执行后将会出现什么结果?
      

  4.   

    microsoft的一些文档包括sql2005的文档我也看过一些,microsoft的确也建议尽量用参数化+存储,但MS同时还建议要将' -- /* 这些特殊字符通过代码人工过滤。
      

  5.   

    这位朋友用 存储 测试还是能注入。http://topic.csdn.net/t/20050705/08/4123059.html
      

  6.   

    存储过程能免受SQL注入攻击。这是不对,只能阻止某些种类的攻击
    存储过程如果使用未筛选的输入,容易受攻击。
    使用存储过程,则应使用参数Parameter作为存储过程的输入
    测试输入的大小和数据类型,强制执行适当的限制。。 
    测试字符串变量的内容,只接受所需的值。 
    绝不直接使用用户输入内容来生成 Transact-SQL 语句。 
    使用存储过程来验证用户输入。 
    在多层环境中,所有数据都应该在验证之后才允许进入可信区域。
    实现多层验证。
      

  7.   

    高手说的就是有条有理,看了N多资料,发现关键是
    “参数Parameter”(甚至有人说用了参数Parameter,用不用存储都无所谓了),
    不过,既然要执行某个需要参数的存储过程,如果不用“Parameter”,还有哪种方式能传递参数呢?是不是像这种:
    With  mycmd   
      .ActiveConnection   =  conn   
      .CommandType   =   4 
      .CommandText   =   "oneSP"  //存储名
      End   With   
     mycmd.Execute ,array(name) //name为参数如果用Parameter,它发挥作用的关键在哪里呢?难道只是“限制了输入变量的长度与数据类型”这一点点作用?
      

  8.   

    修改了楼主的代码,写个示例就知道如何注入:declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1="a';select 123 -- or 1=1"
    set @sql="select * from sysobjects where name='"+@p1+"'"
    exec(@sql)
    go
    这里,原本是要在变量@p1中保存待查找的name的值,结果有效地执行了 “select 123”这个命令。可以执行更加复杂的命令,例如通过SQL Server的xp_cmdshell过程执行DOS命令,或者使用一条update来更改数据。这表明只要是这种“拼接SQL语句”就是祸根。存储过程中如果有程序员写出这种代码,也一样会被注入。
      

  9.   

    我把代码写的更复杂一点看得更清楚:declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1="a';select 123 --"
    set @sql="select * from sysobjects where name='"+@p1+"' and id=-103"
    exec(@sql)
    go
      

  10.   

    但是我要强调的是,你一个小开发人员凭什么@p1就不能有那些符号?明明是你代码写的问题,不要把责任推卸到什么用户界面没有想当然地过滤什么字符之类上面去。一个合格的开发人员写这段代码大致应该写为:declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1="a';select 123 --"
    set @sql="select * from sysobjects where name='"+replace(@p1,"'","''")+"' and id=-103"
    exec(@sql)
    go
    人家SQL Server语法明明说必须把字符串常量中的单引号替换为双引号,那么你写"set @sql ...."这行代码的程序员如果你没有足够详细的分析和设计文档记载着@p1绝对不能有单引号,你就应该不管@p1中有没有单引号都要从语法正确角度出发去使用replace函数先对变量值进行处理。这纯粹是一个SQL Server语法懂与不懂的问题,被夸大到SQL注入问题上来,变得非常可笑。我们就要重写这样的SQL代码,跟程序员追问:你写的这段代码就应该能够处理包括单引号的变量,不要拿“过滤单引号”做借口,那是界面开发的事,架构师想过滤什么字符就可以在界面那里配置过滤什么字符,跟你写数据库查询没有关系。当不小心把数值定义到字符串变量中,我们可以写:declare @p1 varchar(1000)
    declare @sql varchar(1000)
    declare @id varchar(20)
    set @p1="a';select 123 --"
    set @id="-102"
    set @sql="select * from sysobjects where name='"+replace(@p1,"'","''")+"' and id="+
            cast(cast(@id as int) as varchar)
    exec(@sql)
    go
    使用两个cast首先验证@id确实是一个int,然后再重新生成字符串,最后才拼接到SQL。
      

  11.   

    对于那些拼接SQL语句的开发人员,我们首先就要测试其字符串变量中包含单引号的时候会不会出错,如果出错那么就根本不合T-SQL语法了,因为你什么时候见过一个数据库的SQL中的字符串变量不能内含正常的ascii符号的呢?这个正常情况首先你要严格测试通过,让你今后所编写的SQL语句语法首先是正确的,然后再讨论高级问题。对于这个问题,我希望不要从SQL Server注入那么高级的角度,那纯粹是转移SQL Server语句开发人员的低级错误的基本问题了。
      

  12.   

    人家SQL Server语法明明说必须把字符串常量中的单引号替换为双引号 -->  人家SQL Server语法明明说必须把字符串常量中的单引号替换为两个单引号
      

  13.   

    一般的开发人员都懒得去管什么sql注入了.他们没有留这个愚蠢的漏洞的习惯.只有学生才会.
      

  14.   

    楼主还是亲身测试一下吧,用参数传递后,英文的单引号就会变成中文的单引号,也就是说'变成了‘,已经作了转换,sql 语句拼接下来也不会有问题的了!
      

  15.   

    这个有点意思,很少有人提到,我查了这么多资料,只是偶尔发现有人提到参数传递会进行一些过滤.和这位朋友说的倒是相符,不知 ;分号还有--这些符号会不会被处理.另外,Parameters传递是不是只起到了对一些特殊字符进行转换/替换处理的功能,而并没有其它特殊机制让"存储过程总是将传入的参数当一个值处理,而不会执行"
      

  16.   

    关键是parameter 和存储过程就根本没有关系 不妨写几个注入语句 亲身体验之后就知道了
      

  17.   

    LZ这个问题就好比杀毒软件和病毒.目前还没有哪款杀毒软件能真正做到把病毒拘之系统之外.MS几乎每个月都有补丁.但是还是有那么多的攻击/
    存储过程+参数不是万能的.但相对于你的拼接SQL是要安全的很多..
      

  18.   

    首先 单纯的存储过程根本就不能防SQL注入!
    倒是 参数可以过滤SQL注入。
      

  19.   

    额,参数也要看什么参数的,楼主说的有一定道理
    但是传参不是这么拼凑sql啊cmd.Parameters.Add("@Tags", System.Data.SqlDbType.NVarChar, 255).Value = xxx;
    实际上类似这样的都可以过滤,'被替换成''等等..
    不过不要以为这样就安全了 create proc xxxx
    ...
     @SearchSQL NText,
    ....
    AS
    ....Insert  into #IDs(PhotoID) exec(@SearchSQL)
    像这种参数,在存储过程中执行sql,可以被注入..不信试试
      

  20.   

    恩,参数化查询不应该这么拼接sql
    @p1="a' or 1=1"  
    set @sql="select * from table where name='"+[@p1]+"'" 
    exec(@sql)
    正确的方法应该是
    sql = "select * from table where name=@name"
    cmd.Parameters("@name" ........)如果是用存储过程,应该在存储过程里面执行 select * from table where name=@name
    而不是exec(@sql),这样和普通拼接sql没有什么区别实际应用根据要求来定,比如模糊搜索功能,用LIKE '%words%',这种时候应该进行安全过滤
      

  21.   

    那么like查询,不用exec,还能怎么样呢??
      

  22.   

    是呀,参数怎么会是这个样子的呢name='"+[@p1]+"'"直接了name=@p1
      

  23.   

    为什么要在存储过程中再构造sql语句呢,如果构件为什么要用存储过程呢?
      

  24.   


    如果对你来说参数传递 ,只有拼sql 一条路的话,无语了。像你这样 如果查询的是篇 有关sql 的论文 ,里面有很多 sql 关键字 ,估计 楼主 要过滤字符串是 要头疼了吧。
      

  25.   

    如果sql语句是拼凑起来的,不管使用那种方式都有被注入的可能。
    参数+存储过程个人理解为:
    数据库里的存储过程应该写成:
    Create Proc Procedure_Name
    @Param1
    As
    Begion
     select * from table where column1=@Param1
    End
    使用时给参数赋值即可,而非:
    @p1="a' or 1=1"  
    set @sql="select * from table where name='"+[@p1]+"'" 
    exec(@sql) 
      

  26.   

    代碼是自己寫的你用了存儲過程 + 參數但是,又是你自己在存儲過程里把sql 拼起來的這樣都能怪ms?????
      

  27.   

    你这不还是在拼sql语句吗?与程序拼的区别是地方不同了而已?使用存储过程防注入就可要程序员不要拼sql语句.
    如果这也算漏洞,你怎么不把sa的密码置为空?
      

  28.   

    存储过程不拼语句,那么请问like查询这种,不拼参数,怎么查询?
    谢谢
      

  29.   

    请问有人回答下我么,like查询,用存储过程,怎么样可以不拼SQL语句?谢谢
      

  30.   

    听说向Microsoft报告BUG是有钱拿的
      

  31.   

            public static string toSqlLike(this string matchString)
            {
                return matchString == null || matchString.Length == 0 ? matchString : ((new Regex(@"(\[|\]|\*|_|%)")).Replace(matchString, "[$1]").Replace("'", "''"));
            }传的like key参数经过toSqlLike格式化后,
    select * from TABLE where KEY like '%'+@key+'%'
      

  32.   

    当然你也可以不调用toSqlLike,在sql语句里面replace,自己看着办
      

  33.   

    存储过程不负责处理注入,处理了的应当是是SqlParameter在进行操作。实际上都是人云亦云,如果只对于SqlParameter而言,存储过程与SQL语句有什么区别?
      

  34.   

    使用参数方式
    SqlCommand command = conn.CreateCommand();
                command.CommandType = CommandType.Text;
                command.CommandText = "select * from tab_Test where NId=@id";
                SqlParameter param = new SqlParameter("@id", SqlDbType.NVarChar, 50);
                param.Value = TextBox1.Text;
                command.Parameters.Add(param);TextBox1赋值 1 最终执行的SQL语句是
    exec sp_executesql N'select * from tab_Test where NId=@id',N'@id nvarchar(50)',@id=N'1'TextBox1赋值 "a' or 1=1" 最终执行的SQL语句是 
    exec sp_executesql N'select * from tab_Test where NId=@id',N'@id nvarchar(50)',@id=N'"a'' or 1=1"'
      

  35.   

    这个回复有价值,至少让我明白,为什么说用parames传参会安全一些(人家给出了最终执行的SQL是什么),在存储过程中,我也可以按照最终执行SQL代码格式来编写了.
      

  36.   

    感谢这朋友,不是简单地说 NO,而是指出 just do itBegin
    select * from table where column1=@Param1
    End
    这样是不是就使param1只能当作一个值来处理,而不是成了执行代码的一部分?
      

  37.   

    在代码里写sql命令时,对于字符类变量往往需要写成 name='"+myname变量+"'" 的形式,否则会报错。
    不知对于参数,写成name=@name而不加两侧单引号会不会报错。
      

  38.   

    存储过程里面你再去拼sql,只能说很无语。  request 取值的时候replace ' 如果是Int或者是Int64的 全部Convert一次,我看怎么注入。。还有一个就是不知道怎么防范的,别人把注入代码加密成2进制代码的时候怎么防范,这个大家来讨论下。
      

  39.   

    如果不是like模糊查询,只是类似条件“=”的查询都可以写为参数化查询 select ... from ..where name=@name,然后通过 SqlParameter对参数赋值,不需要什么单引号了,所有输入都作为一个值来识别,并非拼接sql
    就像你写一个函数的参数一个,参数里面包含引号,赋值过去难道会出错吗?
      

  40.   


    如果对于数值类的参数,当然处理很简单了.最麻烦的就是字符串上.至于转换成二进制码的SQL注入倒是没听说过,好象有转换成ascII码的,至于如何防范没见到相关资料.
    不过个人分析了一下,发现这种转换后往往会带有%字符,如果将%进行转换或过滤,可能会有一定作用.
      

  41.   

    楼主@p1="a' or 1=1"  
    set @sql="select * from table where name='"+[@p1]+"'" 
    exec(@sql) 你这样能建立存储过程吗,语法都不对。你试过了吗还有说不拼接sql,不拼接,能形成完整的sql语句用参数,只能说MS给我们做了一些过滤,SqlParameter规定了你传参数的大小类型
    严格过滤还得你自己来
      

  42.   


    这只是一个例子,你不妨试试下面这代码declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1='a' + char(39) + ';select 123 --'
    set @sql='select * from sysobjects where name='''+@p1+''' and id=-103'
    exec(@sql)
    go
      

  43.   


    或者是这样
    declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1=''' + char(39) + '''';select 123 --'
    set @sql='select * from sysobjects where name='''+@p1+''' and id=-103'
    exec(@sql)
    go
      

  44.   


    但如果将单引号Replace为两个单引号,那就不会出现查询出123的结果了declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1=''''' + char(39) + '''''''';select 123 --'
    set @sql='select * from sysobjects where name='''+@p1+''' and id=-103'
    exec(@sql)
    go
    所以,严格说来,存储过程不会去防止,防止注入的东西就是SqlParameter
      

  45.   

    刚刚回复有误,应当与char(39)没有任何关系
    declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1=''''';select 123 --'
    set @sql='select * from sysobjects where name='+@p1+' and id=-103'print @sql
    exec(@sql)
    go
      

  46.   


    唉,万恶之源还是这种拼接SQL语句的方式将@p1里面的一个单引号替换为两个单引号,还是会出现123
    declare @p1 varchar(1000)
    declare @sql varchar(1000)
    set @p1=''''''''';select 123 --'
    set @sql='select * from sysobjects where name='+@p1+' and id=-103'print @sql
    exec(@sql)
    go
      

  47.   

    SqlParameters 这个确实是可以防注入的吧。反正我们系统里都是用的这个。要是这个有问题,楼主别忘记跟我说下哟。以免也被注入咯~~~~
      

  48.   

    这种情况我很久以前试过,是不能防注入的。
    但这没有实质的参数化,还是拼接字符串,真正的参数化是直接:
    select * from table where name=@p1