本文来自:caodonglin

一、SQL参数化为什么能防注入?

因为执行计划被重用了,所以可以防SQL注入。

下面有两段SQL

    正常SQL:

1
select COUNT(1) from CruiseSysUser where CSUPwd = 'aa' and CSUUserName = 'bb'

    被注入后的SQL:

1
select COUNT(1) from CruiseSysUser where CSUPwd = 'aa' and CSUUserName = 'bb' or 1=1—'

可以发现被注入后的SQL语义发生了变化,查询逻辑与正常SQL不一致。因为没有重用以前的执行计划,因为对注入后的SQL语句重新进行了编译,因为重新执行了语法解析。拼接的SQL基本上无法保证你写的SQL所表示的意思就是你要表达的意思。任何拼接的SQL都有被注入的风险。

    SQL参数化:

1
select COUNT(*) from CruiseSysUser where CSUPwd = @CSUPwd and CSUUserName =@CSUUserName

如果参数化SQL后,每次执行的都是相同的SQL,SQL的语义不会变化,所以会重用到以前的执行计划。

SQL参数化后,等于是做填空题,不管输入什么条件,我所表达的意思都不变。

存储过程也是一样道理,可以重用执行计划。

二、修改注意点

    1、exec动态执行SQL是不能防SQL的

1
2
3
comm.CommandText = "exec('select * from Users(nolock) where UserID in ('+@UserID+')')";
comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });
comm.ExecuteNonQuery();

这种方式跟拼接SQL没什么区别,SQL语义还是会变化,一样不会重用到执行计划

    2、所有参数都需要参数化

不管是数值还是字符类型,都需要参数化,不只字符串类型能被注入,数值类型同样会被注入

如果前台是下拉框的查询条件,同样需要SQL参数化

3、where in参数化查询

where in语句是没法使用SQL参数化的,是会报错的。以下多种方案推荐使用方案三

方案一:多条件查询

    将SQL改成多个条件,用or,

    方案二:使用临时表

    这种方案比较繁琐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    string sql = @"
        declare @Temp_Variable varchar(max)
        create table #Temp_Table(Item varchar(max))
        while(LEN(@Temp_Array) > 0)
        begin
            if(CHARINDEX(',',@Temp_Array) = 0)
            begin
                set @Temp_Variable = @Temp_Array
                set @Temp_Array = ''
            end
            else
            begin
                set @Temp_Variable = LEFT(@Temp_Array,CHARINDEX(',',@Temp_Array)-1)
                set @Temp_Array = RIGHT(@Temp_Array,LEN(@Temp_Array)-LEN(@Temp_Variable)-1)
            end    
        insert into #Temp_Table(Item) values(@Temp_Variable)
        end    
        select * from Users(nolock) where exists(select 1 from #Temp_Table(nolock) where #Temp_Table.Item=Users.UserID)
        drop table #Temp_Table";
    comm.CommandText = sql;
    comm.Parameters.Add(new SqlParameter("@Temp_Array", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });
    comm.ExecuteNonQuery();
}

方案三:拆分多个参数–推荐写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    //为每一条数据添加一个参数
    comm.CommandText = "select * from Users(nolock) where UserID in (@UserID1,@UserId2,@UserID3,@UserID4)";
    comm.Parameters.AddRange(
    new SqlParameter[]{                        
        new SqlParameter("@UserID1", SqlDbType.Int) { Value = 1},
        new SqlParameter("@UserID2", SqlDbType.Int) { Value = 2},
        new SqlParameter("@UserID3", SqlDbType.Int) { Value = 3},
        new SqlParameter("@UserID4", SqlDbType.Int) { Value = 4}
    });
    comm.ExecuteNonQuery();
}

可以写成通用方法

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private int GetData3(string id)        
{            
    string connectionString = "";            
    using (SqlConnection conn = new SqlConnection(connectionString))            
    {                
        var whereInParam = BuildWhereInStringAndParams<string>(id.Split(new char[] { ',' }, 
        StringSplitOptions.RemoveEmptyEntries), "name");                
        if (whereInParam == nullreturn 0;                
        conn.Open();                
        SqlCommand comm = new SqlCommand();                
        comm.Connection = conn;                
        comm.CommandText = string.Format("select * from CruiseSysUser where CSUUserTCNum in ({0})", whereInParam.Item1);                
        comm.Parameters.AddRange(whereInParam.Item2);                
        return comm.ExecuteNonQuery();            
    }        
}
     
private Tuple<string, SqlParameter[]> BuildWhereInStringAndParams<T>(T[] values, string nameFixed = "SplitName")
{            
    if (values == null || values.Length == 0) return null;            
    StringBuilder sbParams = new StringBuilder();            
    List<SqlParameter> paramList = new List<SqlParameter>();            
    for (int i = 0; i < values.Length; i++)            
    {                
        string paramName = string.Format("@{0}{1}", nameFixed, (i + 1).ToString());                
        if (sbParams.Length > 0) sbParams.Append(",");                
        sbParams.AppendFormat(paramName);                
        paramList.Add(new SqlParameter(paramName, values[i]));            
     }            
     return new Tuple<string, SqlParameter[]>(sbParams.ToString(), paramList.ToArray());        
}

 

 

    方案四:Dapper写法

1
2
3
    string sql = "select * from TCCruisePublic.dbo.CruiseSysUser where CSUUserName in @names";
    dynamic param = new { names = new string[] { "aa'""bb""cc" } };
    var res = context.Query(new SqlNoteInfo(""""""), sql, param as object);

其实dapper的写法,最终转换出来执行的SQL跟方案三是一样的,只是dapper自动做了个转换。

4、like参数化查询
        1、将%写到参数值中

1
2
3
4
5
6
7
8
9
10
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    //将 % 写到参数值中
    comm.CommandText = "select * from Users(nolock) where UserName like @UserName";
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, 200) { Value = "rabbit%" });
    comm.ExecuteNonQuery();
}

2、在SQL中拼接%

1
2
3
4
5
6
7
8
9
10
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    //SQL中拼接 %
    comm.CommandText = "select * from Users(nolock) where UserName like @UserName+'%'";
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar, 200) { Value = "rabbit" });
    comm.ExecuteNonQuery();
}

 5、指定参数类型、参数长度
    SqlParameter参数不加SqlDbType属性的话,托管代码在执行过程中不能自动识别参数类型,进而对该字段内容进行全表扫描以确定参数类型并进行转换,消耗了不必要的查询性能。如果未在size参数中显式设置Size,则从dbType参数的值推断出该大小,不同的Size会导致不重用数据库执行计划。所以一般情况下都要指定SqlDbType与Size。

SQL参数化的更多相关文章

  1. Jmeter的JDBC Request,sql参数化及返回值取值

    1.JDBC Request面板 Variable Name:数据库连接池的名字,需要与JDBC Connection Configuration的Variable Name Bound Pool名字 ...

  2. [转帖] SQL参数化的优点 CopyFrom https://www.cnblogs.com/-lzb/articles/4840671.html

    梦在远方的小猪 感谢原作者...  后面总结的五点感觉挺好的.. 自己之前的知识点一直没有串起来. 转帖记录一下感谢. sql参数化参数化 说来惭愧,工作差不多4年了,直到前些日子被DBA找上门让我优 ...

  3. PatePoco中对sql参数化时Top参数化的问题

    PatePoco中对sql参数化是直接用@+参数名来处理,但是想用如下语句时竟然报错了 SELECT TOP @num * FROM tableA 执行时抛出异常,根据错误提示搞了很久都没找到原因,最 ...

  4. sql参数化防止sql注入导致的暴露数据库问题

    #转载请联系 假如你在京东工作,你要做的任务就是做一个商品搜索的东西供用户使用. 然后你写出了这么一个程序的雏形. import pymysql def main(): conn = pymysql. ...

  5. mybatis的sql参数化查询

    我们使用jdbc操作数据库的时候,都习惯性地使用参数化的sql与数据库交互.因为参数化的sql有两大有点,其一,防止sql注入:其二,提高sql的执行性能(同一个connection共用一个的sql编 ...

  6. SQL参数化查询自动生成SqlParameter列表

    string sql = @"INSERT INTO stu VALUES (@id,@name) "; 参数化查询是经常用到的,它可以有效防止SQL注入.但是需要手动去匹配参数@ ...

  7. 使用 ODBC .NET 提供程序和 Visual C# .NET 执行 SQL 参数化存储过程

    http://support2.microsoft.com/kb/310130/zh-cn 此分步指导文章描述如何使用 ODBC .NET 托管提供程序和 Visual C# .Net 调用参数化 S ...

  8. SQL 参数化查询 应用于 Like

    在sql 进行参数化查询的时候,使用like 语句和参数的时候,错误的写法:  Participant like '%@Participant%' ,这样在数据库为解析为 '%'participant ...

  9. SQL参数化查询--最有效可预防SQL注入攻击的防御方式

    参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值. 在使用参 ...

随机推荐

  1. ViewPager顶部标题控件PagerSlidingTabStrip

    最近搞一个项目,要求做一个和网易新闻顶部菜单的滑动效果,如图: 顶部标题中下面有个红色的矩形小条,左右滑动时会跟随手势动态滑动,效果很绚丽,唉,特效啊! 自己搞了一上午无果,还是是github上找大神 ...

  2. Oracle—RMAN备份(一)

    一.RMAN备份相关概念 1.RMAN备份中表空间不需要处于backup模式下,它备份数据文件,归档日志文件,控制文件,spfile和备份集片,但不备份联机重做日志文件,临时文件和口令文件. 2.备份 ...

  3. 安装centos6.3

    废话少说,今天安装镜像文件.版本为centos6.3 1.首先,我们已经创建了一个空的虚拟机,此时,打开虚拟机,选择的镜像文件,点击ok自己下载 2.点击绿色的三角箭头,你会看到下面页面.(如果报错T ...

  4. [转]iOS开发使用半透明模糊效果方法整理

    转自:http://www.molotang.com/articles/1921.html 虽然iOS很早就支持使用模糊效果对图片等进行处理,但尤其在iOS7以后,半透明模糊效果得到大范围广泛使用.包 ...

  5. mysql 5.6 设置慢查询

    mysql 5.6 开启慢查询日志 slow_query_log = on #开启慢查询 1 或者 on long_query_time = 3 #记录超过的时间,单位是秒,默认是10s slow_q ...

  6. 注册DLL,Unregister DLL

    前一篇文章,反复注册,反注册.... 写了一个小工具 怎么传图片感觉不对劲,StatusBar应改成 拖动DLL至上方 文件 http://files.cnblogs.com/magicdawn/Dl ...

  7. (转)Javascript面向对象编程(二):构造函数的继承(作者:阮一峰)

    对象之间的"继承"的五种方法. 比如,现在有一个"动物"对象的构造函数. function Animal(){ this.species = "动物& ...

  8. ZigZag-LeetCode

    题目: The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows l ...

  9. “Win”组合键

    Windows组合键功能: 单独按下显示或隐藏 [开始] 功能表. +Break 显示 [系统内容] 对话方块. +D 显示桌面. +M 最小化所有的视窗. +Shift+M 还原最小化的视窗. +E ...

  10. FCKeditor插件开发实例:uploadify多文件上传插件

    FCKeditor是一个专门使用在网页上属于开放源代码的所见即所得文字编辑器.它志于轻量化,不需要太复杂的安装步骤即可使用.它可和PHP.JavaScript.ASP.ASP.NET.ColdFusi ...