SQL参数化
本文来自: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 == null) return 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参数化的更多相关文章
- Jmeter的JDBC Request,sql参数化及返回值取值
1.JDBC Request面板 Variable Name:数据库连接池的名字,需要与JDBC Connection Configuration的Variable Name Bound Pool名字 ...
- [转帖] SQL参数化的优点 CopyFrom https://www.cnblogs.com/-lzb/articles/4840671.html
梦在远方的小猪 感谢原作者... 后面总结的五点感觉挺好的.. 自己之前的知识点一直没有串起来. 转帖记录一下感谢. sql参数化参数化 说来惭愧,工作差不多4年了,直到前些日子被DBA找上门让我优 ...
- PatePoco中对sql参数化时Top参数化的问题
PatePoco中对sql参数化是直接用@+参数名来处理,但是想用如下语句时竟然报错了 SELECT TOP @num * FROM tableA 执行时抛出异常,根据错误提示搞了很久都没找到原因,最 ...
- sql参数化防止sql注入导致的暴露数据库问题
#转载请联系 假如你在京东工作,你要做的任务就是做一个商品搜索的东西供用户使用. 然后你写出了这么一个程序的雏形. import pymysql def main(): conn = pymysql. ...
- mybatis的sql参数化查询
我们使用jdbc操作数据库的时候,都习惯性地使用参数化的sql与数据库交互.因为参数化的sql有两大有点,其一,防止sql注入:其二,提高sql的执行性能(同一个connection共用一个的sql编 ...
- SQL参数化查询自动生成SqlParameter列表
string sql = @"INSERT INTO stu VALUES (@id,@name) "; 参数化查询是经常用到的,它可以有效防止SQL注入.但是需要手动去匹配参数@ ...
- 使用 ODBC .NET 提供程序和 Visual C# .NET 执行 SQL 参数化存储过程
http://support2.microsoft.com/kb/310130/zh-cn 此分步指导文章描述如何使用 ODBC .NET 托管提供程序和 Visual C# .Net 调用参数化 S ...
- SQL 参数化查询 应用于 Like
在sql 进行参数化查询的时候,使用like 语句和参数的时候,错误的写法: Participant like '%@Participant%' ,这样在数据库为解析为 '%'participant ...
- SQL参数化查询--最有效可预防SQL注入攻击的防御方式
参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值. 在使用参 ...
随机推荐
- Pig系统分析(6)-从Physical Plan到MR Plan再到Hadoop Job
从Physical Plan到Map-Reduce Plan 注:由于我们重点关注的是Pig On Spark针对RDD的运行计划,所以Pig物理运行计划之后的后端參考意义不大,这些部分主要分析流程, ...
- html的特质语义:微格式及其他(重点介绍其中两种)
今天再次翻开html的书本, 感觉过了个周末似乎生疏了许多, 虽然我是刚接触html的, 但是对于他还是抱有极其大的兴趣的, 所以不爱看书的我, 也开始一遍遍的翻阅着书本, 寻找解决问题的方法, 下面 ...
- Hacker(24)----防范密码被轻易破解
无论什么类型密码,用户在设置时都有非常小心,防止自己设置的密码被他人轻易破解.为保护重要的文件和资料,可采用加密工具进行加密,即可选择Win7系统自带的BitLocker,也可使用Internet中很 ...
- DEV SIT UAT
DEV环境:DEV顾名思义就是develop,即代码开发的环境.SIT环境:System Integration Test系统集成测试,开发人员自己测试流程是否走通.UAT环境:User Accept ...
- 再关于IE11
微软在上周刚刚发布了用于Windows 8.1上的首个Internet Explorer 11的预览版.我们已经确认Internet Explorer 11中的一些新特性,包括对WebGL的支持.预抓 ...
- respondsToSelector的使用
- (BOOL)respondsToSelector:(SEL)aSelector; 用来判断是否有以某个名字命名的方法 +(BOOL) instancesRespondToSelector: sel ...
- BestCoder Round #36 (hdu5199)Gunner(水题)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud Gunner Time Limit: 8000/4000 MS (Java/Oth ...
- windows下自动删除n天前的文件
使用windows2003下的内置命令forfiles配合计划任务可以实现自动删除n天前的文件. windows2003中设定自动执行的计划任务很简单. 一.脚本编写 forfiles命令用法: Fo ...
- overflow应用随记
今天在帮别人改页面时遇到了overflow属性,虽然对他已经比较熟悉了,但还是去专门查找了一下.和大家分享下. overflow 属性规定当内容溢出元素框时发生的事情. 这个属性定义溢出元素内容区的内 ...
- php如何返回一个image文件
The important points is that you must send a Content-Type header. Also, you must be careful not incl ...