一大清早的惊醒,看别人SQL注入之后,加强学习SQL和参数化查询。
2012-03-31 09:57
381 查看
先说说两会(这名字不怕被河蟹)的帖子哈:/article/5316801.html
忽然发现,很多项目中还使用这种拼SQL语句的方式。真是早就该进步啊。以前一师兄说过用参数化查询,我没有仔细揣摩参数化查询的本质。知其然不知其所以然,那么看完这帖子,我的困觉一下的又醒了。马上来仔细讨论学习一下SQL参数化查询。
引用:
参数化查询
参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值。
在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在数据库完成SQL指令的编译后,才 套用参数运行,因此就算参数中含有指令,也不会被数据库运行。Access、SQL Server、MySQL、SQLite等常用数据库都支持参数化查询。
什么是参数化查询?
一个简单理解参数化查询的方式是把它看做只是一个T-SQL查询,它接受控制这个查询返回什么的参数。通过使用不同的参数,一个参数化查询返回不同的结果。要获得一个参数化查询,你需要以一种特定的方式来编写你的代码,或它需要满足一组特定的标准。
有两种不同的方式来创建参数化查询。第一个方式是让查询优化器自动地参数化你的查询。另一个方式是通过以一个特定方式来编写你的T-SQL代码,并将它传递给sp_executesql系统存储过程,从而编程一个参数化查询。
参数化查询被喻为最有效防止SQL注入的方法,那么存储过程一定是参数化过后的吗?
如果存储过得利用传递进来的参数,再次进行动态SQL拼接,这样还算做是参数化过后的吗?如果存储过程一定是参数化过后的,那么是不是意味着,只要使用存储过程就具有参数化查询的全部优点了?
这种方法没有办法防止注入,你能做的就是对字符串进行过滤.
拼接SQL是:
"select * from customer where 1=1" + " and name=@name" + " and sex=@sex"
也就是判断参数化查询。只不过是动态地组装查询限制条件。
动态拼接SQL,而且是参数化查询的SQL语句是没有问题的。
ADO.NET中被SQL注入的问题,必须过于关键字。我的测试代码如下:
忽然发现,很多项目中还使用这种拼SQL语句的方式。真是早就该进步啊。以前一师兄说过用参数化查询,我没有仔细揣摩参数化查询的本质。知其然不知其所以然,那么看完这帖子,我的困觉一下的又醒了。马上来仔细讨论学习一下SQL参数化查询。
引用:
以往的防御方式 以前对付这种漏洞的方式主要有三种: 字符串检测:限定内容只能由英文、数字等常规字符,如果检查到用户输入有特殊字符,直接拒绝。但缺点是,系统 中不可避免地会有些内容包含特殊字符,这时候总不能拒绝入库。 字符串替换:把危险字符替换成其他字符,缺点是危险字符可能有很多,一一枚举替换相当麻烦,也可能有漏网之 鱼。 存储过程:把参数传到存储过程进行处理,但并不是所有数据库都支持存储过程。如果存储过程中执行的命令也是通 过拼接字符串出来的,还是会有漏洞。 |
参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值。
在使用参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的一部份来处理,而是在数据库完成SQL指令的编译后,才 套用参数运行,因此就算参数中含有指令,也不会被数据库运行。Access、SQL Server、MySQL、SQLite等常用数据库都支持参数化查询。
在ASP.NET程序中使用参数化查询 ASP.NET环境下的查询化查询也是通过Connection对象和Command对象完成。如果数据库是SQL Server,就可以用有名字的参数了,格式是“@”字符加上参数名。 SqlConnection conn = new SqlConnection("server=(local)\\SQL2005;user id=sa;pwd=12345;initial catalog=TestDb"); conn.Open(); SqlCommand cmd = new SqlCommand(“SELECT TOP 1 * FROM [User] WHERE UserName = @UserName AND Password = @Password“); cmd.Connection = conn; cmd.Parameters.AddWithValue(”UserName”, “user01″); cmd.Parameters.AddWithValue(”Password”, “123456″); SqlDataReader reader = cmd.ExecuteReader(); reader.Read(); int userId = reader.GetInt32(0); reader.Close(); conn.Close(); |
一个简单理解参数化查询的方式是把它看做只是一个T-SQL查询,它接受控制这个查询返回什么的参数。通过使用不同的参数,一个参数化查询返回不同的结果。要获得一个参数化查询,你需要以一种特定的方式来编写你的代码,或它需要满足一组特定的标准。
有两种不同的方式来创建参数化查询。第一个方式是让查询优化器自动地参数化你的查询。另一个方式是通过以一个特定方式来编写你的T-SQL代码,并将它传递给sp_executesql系统存储过程,从而编程一个参数化查询。
参数化查询被喻为最有效防止SQL注入的方法,那么存储过程一定是参数化过后的吗?
如果存储过得利用传递进来的参数,再次进行动态SQL拼接,这样还算做是参数化过后的吗?如果存储过程一定是参数化过后的,那么是不是意味着,只要使用存储过程就具有参数化查询的全部优点了?
我有如下存储过程:create procedure pro_getCustomers ( @whereSql nvarchar(max) ) as declare @sql nvarchar(max) set @sql=N'select * from dbo.Customer ' + @whereSql exec(@sql) Go --如果我要在ADO.NET中参数化查询这个存储过程,以防止SQL注入,我该怎么办呢?比如: exec pro_getCustomers 'where Name=@name' |
拼接SQL是:
"select * from customer where 1=1" + " and name=@name" + " and sex=@sex"
也就是判断参数化查询。只不过是动态地组装查询限制条件。
动态拼接SQL,而且是参数化查询的SQL语句是没有问题的。
ADO.NET中被SQL注入的问题,必须过于关键字。我的测试代码如下:
USE [B2CShop] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER procedure [dbo].[pro_getCustomers] ( @whereSql nvarchar(max), @paramNameList nvarchar(max), @paramValueList nvarchar(max) ) as declare @sql nvarchar(max) set @sql=N'select * from dbo.Customer ' + @whereSql exec sp_executesql @sql, @paramNameList , @paramValueList go |
/// <summary> /// 动态执行存储过程 /// </summary> /// <param name="searchedName">要查询的姓名的关键字</param> /// <returns>实体集合</returns> public static List<Customer> ExecDynamicProc(string searchedName) { SqlParameter[] values = new SqlParameter[] { new SqlParameter("@whereSql", "where name like @name"), new SqlParameter("@paramNameList","@name nvarchar(50)"), new SqlParameter("@paramValueList","@name='%"+ searchedName +"%'") }; return DBHelper.ExecuteProc("proc_GetCustomerPagerBySearch",values); } |
/// <summary> /// 从搜索类里面拼接参数化的SQL字符串 /// </summary> /// <param name="search">搜索类</param> /// <param name="sqlParams">搜索的参数,不能传入Null</param> /// <returns>安全的SQL语句</returns> private static string GetSafeSqlBySearchItem(CustomerSearch search, ref List<SqlParameter> sqlParams) { StringBuilder safeSqlAppend = new StringBuilder(); if (search != null) { if (!string.IsNullOrEmpty(search.NameEquals)) { safeSqlAppend.Append(" and Name=@nameEquals"); sqlParams.Add(new SqlParameter("@nameEquals", search.NameEquals)); } if (!string.IsNullOrEmpty(search.NameContains)) { safeSqlAppend.Append(" and Name like @nameContains"); sqlParams.Add(new SqlParameter("@nameContains", "%" + search.NameContains + "%")); } } return safeSqlAppend.ToString(); } |
/// <summary> /// 得到分页用的SQL语句 /// </summary> /// <param name="columnNameItems">要查询的列名,多个列名用逗号分隔。传入Empty或Null时,则默认查询出所有的列</param> /// <param name="tableName">表名,不能为Null和Empty,默认的SQL别名为a</param> /// <param name="joinOtherTable">连接其他的表,可以传入Null或Empty。调用的时候,可以类似如:inner join departInfo as b on a.departInfoId=b.Id</param> /// <param name="whereSql">搜索条件,即在“where 1=1 ”后面写条件,可以传入Null或Empty。调用的时候,可以类似如:and b.Price=@beginPrice </param> /// <param name="orderColumnNameAndAscOrDesc">排序的列名以及Asc或Desc,即在“order by”后面写排序项,不能为Null和Empty。比如“Id asc, name desc”</param> /// <param name="pageNumber">当前页的页码,最小值应该为1</param> /// <param name="pageSize">每页显示的记录数,最小值应该为1</param> /// <returns>SQL语句</returns> internal static string GetPagerTSql(string columnNameItems, string tableName, string joinOtherTable, string whereSql, string orderColumnNameAndAscOrDesc, int pageNumber, int pageSize) { if (string.IsNullOrEmpty(tableName)) { throw new ArgumentNullException("tableName", String.Format(CultureInfo.CurrentCulture, DALResource.Common_NullOrEmpty)); } if (string.IsNullOrEmpty(orderColumnNameAndAscOrDesc)) { throw new ArgumentNullException("orderColumnNameAndAscOrDesc", String.Format(CultureInfo.CurrentCulture, DALResource.Common_NullOrEmpty)); } if (string.IsNullOrEmpty(columnNameItems)) { columnNameItems = "a.*"; } if (pageNumber < 1) { pageNumber = 1; } if (pageSize < 1) { pageSize = 1; } int beginNumber = (pageNumber - 1) * pageSize + 1; int endNumber = pageNumber * pageSize; string sqlPager = string.Format("select * from (select row_number() over(order by {1}) as __MyNewId, {0} from {2} as a {3} where 1=1 {4}) as __MyTempTable where __MyNewId between {5} and {6} order by __MyNewId asc;", columnNameItems, orderColumnNameAndAscOrDesc, tableName, joinOtherTable, whereSql, beginNumber, endNumber); string sqlPagerCount = string.Format("select @__returnCount=COUNT(*) from {0} as a {1} where 1=1 {2};",tableName, joinOtherTable, whereSql); return sqlPager + sqlPagerCount; } |
相关文章推荐
- 通过sqli-labs学习sql注入——基础挑战之less11-22
- 【黑马程序员】参数化查询,避免sql注入漏洞攻击(学习笔记)
- SQL 数据库 学习 028 查询-11 having --- 对分组之后的信息进行过滤
- LINQ To SQL深入学习系列之三(C#3.0为LINQ的加强之三表达式树)
- LINQ To SQL深入学习系列之二(C#3.0为LINQ的加强之二)
- 使用t-sql从身份证号中提取生日(转自别人,个人学习收藏用)
- SQL学习笔记[4] - SQL Server2008附加数据库之后显示为只读时解决方法
- 通过sqli-labs学习sql注入——进阶挑战之less23-28a
- LINQ To SQL深入学习系列之一(C#3.0为LINQ的加强之一)
- ADO.NET学习之防止SQL注入,存储过程,SqlDataReader
- 经典SQL语句大全 收集别人的主要是为了学习的时候查找方便
- 别人推荐的SQL书,留着,慢慢学习
- LINQ To SQL深入学习系列之二(C#3.0为LINQ的加强之二)
- 黑马程序员-Ado.net学习之参数化查询(防止sql注入)
- 通过sqli-labs学习sql注入——基础挑战之less1-10
- LINQ To SQL深入学习系列之一(C#3.0为LINQ的加强之一Lambda 表达式)
- Sqli-labs学习SQL注入-Lesson 1-10总结
- LINQ To SQL深入学习系列之一(C#3.0为LINQ的加强之一)
- 【原创】Microsoft SQL Server 2005技术内幕:T-SQ程序设计 学习笔记
- T-SQL学习笔记