★用存储过程防止SQL注入是笑话吗?

newdigitime 2009-08-08 07:21:19
以前和大家一样,听人说用存储过程+参数能防止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命令有何区别?

搜了一下资料,说参数+存储安全性高的大有人在,但认为参数+存储在安全性上并没多大意义的人也不少
,他们认为仍需要进行严格的字符过滤。大家上网搜一下就能看到了。

实在是很困惑,请大家各持己见。谢谢。尽量不要用“可以,因为参数是传值”或"你不了解参数的意义"等空洞的回答,再次感谢。
...全文
8248 196 打赏 收藏 转发到动态 举报
写回复
用AI写文章
196 条回复
切换为时间正序
请发表友善的回复…
发表回复
w7758520 2012-06-02
  • 打赏
  • 举报
回复
一直用参数形式,但是木有过滤~
yixingbo3 2012-05-21
  • 打赏
  • 举报
回复
请问你们这种拼接,如果作为参数穿过来的话,能进行注入吗?在存储过程内部,这样拼接是可以执行的,但是如果是调用存储过程,然后把你注入的sql作为参数放进入,还能达到注入效果吗?
osnot 2012-04-25
  • 打赏
  • 举报
回复
这个帖子太厉害了,现在知道了使用存储过程中的参数是当做值来处理,而不是SQL语句来处理的。谢谢!
oioixiabings 2011-09-04
  • 打赏
  • 举报
回复
mark!
neplove 2011-04-05
  • 打赏
  • 举报
回复
执行语句之前,严格验证用户的输入。查看有没有关键字,符号等。如果有的话返回去,不执行。
rebing 2011-03-30
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 sp1234 的回复:]
但是我要强调的是,你一个小开发人员凭什么@p1就不能有那些符号?明明是你代码写的问题,不要把责任推卸到什么用户界面没有想当然地过滤什么字符之类上面去。

一个合格的开发人员写这段代码大致应该写为:

SQL code
declare @p1 varchar(1000)
declare @sql varchar(1000)
set @p1="a';select 123 --"
set……
[/Quote]

学习了, 以前用存贮过程的时候 都是在程序里直接进行字符过滤,所以一直不理解存贮过程怎么防止注入,谢谢
老彭1980 2011-01-28
  • 打赏
  • 举报
回复
再说的实质一点。拼接的SQL中,如果有一个selest,它会被当做指令执行,因为你传给SQL server的是一个字符串,这个字符串里的SQL指令保留字都会被执行。

为什么参数化不会被注入?因为参数化是用参数来代替字段值,参与指令的编译,编译后才把参数值传递进去,这样这个值就会被当成单纯的值来对待,值里面被恶意加入的SQL指令就不会被执行。这引出了参数化的另一个好处,就是参数化查询是一次编译多次执行,每次查询只把接收到的参数值传递给编译结果就行了,不必重新编译整条指令;而拼接字符串,因为直接把值拼进指令里,所以数据库每次收到的都是不同的查询指令,因此每查询一次就得编译一次。因此,参数化查询的性能也比较好一点。(我用的SQL Server2005和2008,其他版本的情况我不知道)
老彭1980 2011-01-28
  • 打赏
  • 举报
回复
不要被形式迷惑,要明白实质。很多人把避免SQL注入的方法概括为“用参数化”或者“用存储过程+参数化”。其实这种表面化的说法往往误导了初学者。避免SQL注入,应该概括为“避免SQL拼接”。凡是能避免SQL拼接的方法都是好的防注入方法。这是问题的实质,很多人写了几年程序,这个问题就没彻底明白过。
老彭1980 2011-01-28
  • 打赏
  • 举报
回复
注入的根源在拼接。无论是单独使用参数化,还是使用存储过程+参数化,都只是一种形式,其本质是为了提供一个避免拼接的环境。就好比给你一辆法拉利,在客观条件上给了你参加拉力赛的资格,但是能不能参加,还得看你开的怎么样。如果楼主使用存储过程,然后又在存储过程中拼接,等于什么都没做,这不是法拉利的错,是车手的错。
wing3_lee 2010-06-08
  • 打赏
  • 举报
回复
学习了,关注..
ltcszk 2009-12-28
  • 打赏
  • 举报
回复
linq会被注入吗
smmlfl 2009-12-28
  • 打赏
  • 举报
回复
用存储过程确实比用参数传值好多了,但是存储过程用的太多了,可能程序的速度会下降
用单纯的拼接会有很多漏洞的,很容易就让别人sql注入了
http://www.taotao98.com
Terry717 2009-12-28
  • 打赏
  • 举报
回复
受益匪浅
syc958 2009-12-28
  • 打赏
  • 举报
回复
楼主不是没有明白怎么用过程...你这其实也是在拼接sql....
Tony3820 2009-12-23
  • 打赏
  • 举报
回复
一般情况的安全级别,用存储过程加上参数就可以啦,如是实质性的安全级别还 是要过滤一些特定字符串!
rebing 2009-12-23
  • 打赏
  • 举报
回复
学习了~~ 以前一直对用户传输的每个参数都做验证 看来以后有的可以不用验证了 嘿嘿
newdigitime 2009-12-23
  • 打赏
  • 举报
回复
[Quote=引用 119 楼 jack15850798154 的回复:]
顶一下。。

一.尽量使用参数形式,少用拼凑型SQL语句.这不但安全,还增加代码可读性,何乐不为呢!
SqlParameter[] parms = new SqlParameter[] {
                    new SqlParameter(PARM_EMAIL, SqlDbType.VarChar, 80),
                    new SqlParameter(PARM_FIRST_NAME, SqlDbType.VarChar, 80),
                    new SqlParameter(PARM_LAST_NAME, SqlDbType.VarChar, 80),
                    new SqlParameter(PARM_ADDRESS1, SqlDbType.VarChar, 80),
                    new SqlParameter(PARM_ADDRESS2, SqlDbType.VarChar, 50),
                    new SqlParameter(PARM_CITY, SqlDbType.VarChar, 80),
                    new SqlParameter(PARM_STATE, SqlDbType.VarChar, 80),
                    new SqlParameter(PARM_ZIP, SqlDbType.VarChar, 50),
                    new SqlParameter(PARM_COUNTRY, SqlDbType.VarChar, 50),
                    new SqlParameter(PARM_PHONE, SqlDbType.VarChar, 80),
                    new SqlParameter(PARM_USER_ID, SqlDbType.VarChar, 80)};二.转换参数类型.如是参数是数字型,则转换成数字型的才操作.
三.替换特殊字符.如将"'"替换成""".
四.条件可以的话,请使用存储过程操作数据库.
五.等等,当然安全要做的事,远远不止这些..

在这里我还要再发布自已用过的,用于参数及表单过滤的一个类.请大家自已扩展
using System;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic;

namespace zhang.Common
{
    public class AntiSqlInAttack
    {
        public System.Web.HttpRequest request;

        public AntiSqlInAttack(System.Web.HttpRequest request)
        {
            this.request = request;
        }

        public bool CheckBadQuery()
        {
            //整串字符对比方法
            //string badword = ";|'|*|%| and |20%and20%| master |20%master20%|exec|insert|select|delete|count|chr|mid|truncate|char|declare|update";
            //string query = request.ServerVariables["Query_String"].ToString();
            //string[] badwordArry = badword.Split(new char[] { '|' });
            //for (int i = 0; i < badwordArry.Length; i++)
            //{
            //    string tempWord = badwordArry[i].Trim();
            //    if (query.IndexOf(tempWord) >= 1)
            //        return true;
            //}
            //return false;
            if (request.QueryString.Count != 0)
            {
                for (int i = 0; i < request.QueryString.Count; i++)
                {
                    if (CheckBadWord(request.QueryString[i].ToString()))
                        return true;
                }
            }
            return false;
        }

        public bool CheckBadForm()
        {
            if (request.Form.Count > 0)
            {
                for (int i = 0; i < request.Form.Count; i++)
                {
                    if (CheckBadWord(request.Form[i]))
                        return true;
                }
            }
            return false;
        }

        public bool CheckBadWord(string str)
        {
            string pattern = @"select|insert|delete|from|count\(|drop table|update|truncate|asc\(|mid\(|char\(|xp_cmdshell|exec  master|netlocalgroup administrators|:|net user|""|or|and";
            if (Regex.IsMatch(str, pattern, RegexOptions.IgnoreCase) || Regex.IsMatch(str, @"[-|;|,|\/|\(|\)|\[|\]|\}|\{|%|@|\*|!|\']"))
                return true;
            return false;
        }

        /**//// <summary>
        /// 反SQL注入
        /// </summary>
        public void AntiSqlInjectionAttack()
        {
            if (CheckBadQuery() || CheckBadForm())
            {
                string msg = string.Empty;
                msg += " <span style='font-size:12px;'>非法操作!系统做了如下记录! <br>";
                msg += "操作IP:" +Utils.GetRealIP()+ " <br>";
                msg += "操作时间:" + DateTime.Now + " <br>";
                msg += "页面:" + request.ServerVariables["URL"].ToLower() + " <br>";
                msg += " <a href=\"#\" onclick=\"history.back()\">返回上一页 </a> </span>";
                MessageBox.ResponseWrite(msg, true);
            }
        }

    }
}

[/Quote]

不错。我现在重点放在人工处理一些特殊字符和特殊字符串上了,当然参数化是必须的。
newdigitime 2009-12-23
  • 打赏
  • 举报
回复
[Quote=引用 96 楼 jjkk168 的回复:]
引用 95 楼 jjkk168 的回复:
刚刚回复有误,应当与char(39)没有任何关系

SQL codedeclare@p1varchar(1000)declare@sqlvarchar(1000)set@p1=''''';select 123 --'set@sql='select * from sysobjects where name='+@p1+' and id=-103'print@sqlexec(@sql)go


唉,万恶之源还是这种拼接SQL语句的方式

将@p1里面的一个单引号替换为两个单引号,还是会出现123

SQL codedeclare@p1varchar(1000)declare@sqlvarchar(1000)set@p1=''''''''';select 123 --'set@sql='select * from sysobjects where name='+@p1+' and id=-103'print@sqlexec(@sql)go
[/Quote]
我不习惯替换成半角的字符,我直接将单引号替换成全角的‘
将-替换成—
等等
newdigitime 2009-12-23
  • 打赏
  • 举报
回复
[Quote=引用 150 楼 a6711145 的回复:]
你那个存储过程是在数据库里面的存储过程,防止注入,是指在页面中调用参数式写入的方式.当使用参数式写入时,无论你是使用哪一种注入方式,比如or之类的,理论上,参数式写入总将这些语句当做一个单纯的文本来进行写入,所以不会出现你所说的方式. 
  如果使用参数式写入,那么,根本无需在数据库里面写存储过程. 
  如下参考写入方式 
   
        //定义SQL语句 
        string  sql  =  "insert  into  "  +  this.dbTable  +  "(filename,filenewname,filesize,filemime,fileblob,uploadtime)  "  + 
          "  values(@filename,@filenewname,@filesize,@filemime,@fileblob,@uploadtime)"; 
   
        myComm.CommandText  =  sql; 
        myComm.Parameters.Add("@filename",OleDbType.VarWChar).Value  =  fileName; 
        myComm.Parameters.Add("@filenewname",OleDbType.VarWChar).Value  =  fileNewName; 
        myComm.Parameters.Add("@filesize",OleDbType.Integer).Value  =  fileSize; 
        myComm.Parameters.Add("@filemime",OleDbType.VarWChar).Value  =  fileMime; 
        myComm.Parameters.Add("@fileblob",OleDbType.LongVarBinary,fileBlob.Length).Value  =  fileBlob; 
        myComm.Parameters.Add("@uploadtime",OleDbType.DBDate).Value  =  uploadTime; 
   
        //写入数据库 
        myComm.ExecuteNonQuery(); 
[/Quote]

兄台没看到我对“存储过程可防注”提出“笑话”一说吗?
所以说防注入的重点是"参数化"(参数化后用不用存储,参数也能起它应有的作用),但网上大把的资料是"要防注要用存储"。

不过存储过程也是有一定安全意义的,譬如它没有直接暴露表名以及字段名
另外,在存储过程中对参数/变量进行数据类型及长度定义,也可以对输入字符进行一定的限定。
qroom 2009-12-11
  • 打赏
  • 举报
回复
MYSQL有没有存储过程,有没有人用过?
加载更多回复(173)

62,072

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧