2023年6月21日发(作者:)
SQL参数化查询SQL参数化查询⼀、以往的防御⽅式以前对付这种漏洞的⽅式主要有三种:字符串检测:限定内容只能由英⽂、数字等常规字符,如果检查到⽤户输⼊有特殊字符,直接拒绝。但缺点是,系统 中不可避免地会有些内容包含特殊字符,这时候总不能拒绝⼊库。字符串替换:把危险字符替换成其他字符,缺点是危险字符可能有很多,⼀⼀枚举替换相当⿇烦,也可能有漏⽹之 鱼。存储过程:把参数传到存储过程进⾏处理,但并不是所有数据库都⽀持存储过程。如果存储过程中执⾏的命令也是通 过拼接字符串出来的,还是会有漏洞。⼆、什么是参数化查询? ⼀个简单理解参数化查询的⽅式是把它看做只是⼀个T-SQL查询,它接受控制这个查询返回什么的参数。通过使⽤不同的参数,⼀个参数化查询返回不同的结果。要获得⼀个参数化查询,你需要以⼀种特定的⽅式来编写你的代码,或它需要满⾜⼀组特定的标准。 有两种不同的⽅式来创建参数化查询。第⼀个⽅式是让查询优化器⾃动地参数化你的查询。另⼀个⽅式是通过以⼀个特定⽅式来编写你的T-SQL代码,并将它传递给sp_executesql系统存储过程,从⽽编程⼀个参数化查询。
这样的解释还是有点模糊,先看⼀例:例⼀:参数化查询 参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填⼊数值或数据的地⽅,使⽤参数 (Parameter) 来给值。 在使⽤参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的⼀部份来处理,⽽是在数据库完成SQL指令的编译后,才套⽤参数运⾏,因此就算参数中含有指令,也不会被数据库运⾏。Access、SQL Server、MySQL、SQLite等常⽤数据库都⽀持参数化查询。//在程序中使⽤参数化查询//环境下的查询化查询也是通过Connection对象和Command对象完成。如果数据库是SQL Server,就可以⽤有名字的参数了,格式是“@”字符加上参数名。SqlConnection conn = new SqlConnection("server=(local)SQL2005;user id=sa;pwd=12345;initial catalog=TestDb");();SqlCommand cmd = new SqlCommand(“SELECT TOP 1 * FROM [User] WHERE UserName = @UserName AND Password = @Password“);tion = conn;hValue(”UserName”, “user01″);hValue(”Password”, “123456″);SqlDataReader reader = eReader();();int userId = 32(0);();();参数化查询被喻为最有效防⽌SQL注⼊的⽅法,那么存储过程⼀定是参数化过后的吗?
如果存储过得利⽤传递进来的参数,再次进⾏动态SQL拼接,这样还算做是参数化过后的吗?如果存储过程⼀定是参数化过后的,那么是不是意味着,只要使⽤存储过程就具有参数化查询的全部优点了?
如下存储过程: create procedure pro_getCustomers( @whereSql nvarchar(max))asdeclare @sql nvarchar(max)set @sql=N'select * from er ' + @whereSqlexec(@sql)Go--如果我要在中参数化查询这个存储过程,以防⽌SQL注⼊,我该怎么办呢?⽐如:exec pro_getCustomers 'where Name=@name'这种⽅法没有办法防⽌注⼊,你能做的就是对字符串进⾏过滤.拼接SQL是:
"select * from customer where 1=1" + " and name=@name" + " and sex=@sex"也就是判断参数化查询。只不过是动态地组装查询限制条件。动态拼接SQL,⽽且是参数化查询的SQL语句是没有问题的。中被SQL注⼊的问题,必须过于关键字。原作者的测试代码如下:
USE [B2CShop]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER procedure [dbo].[pro_getCustomers]( @whereSql nvarchar(max), @paramNameList nvarchar(max), @paramValueList nvarchar(max))asdeclare @sql nvarchar(max)set @sql=N'select * from er ' + @whereSqlexec sp_executesql @sql, @paramNameList , @paramValueListgo/// /// 动态执⾏存储过程 /// /// 要查询的姓名的关键字 /// 实体集合 public static List 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 eProc("proc_GetCustomerPagerBySearch",values); }/// /// 从搜索类⾥⾯拼接参数化的SQL字符串 /// /// 搜索类 /// 搜索的参数,不能传⼊Null /// 安全的SQL语句 private static string GetSafeSqlBySearchItem(CustomerSearch search, ref List sqlParams) { StringBuilder safeSqlAppend = new StringBuilder(); if (search != null) { if (!OrEmpty(uals)) { (" and Name=@nameEquals"); (new SqlParameter("@nameEquals", uals)); } if (!OrEmpty(ntains)) { (" and Name like @nameContains"); (new SqlParameter("@nameContains", "%" + ntains + "%")); } } return ng(); }/// /// 得到分页⽤的SQL语句 /// /// 要查询的列名,多个列名⽤逗号分隔。传⼊Empty或Null时,则默认查询出所有的列 /// 表名,不能为Null和Empty,默认的SQL别名为a /// 搜索条件,即在“where 1=1 ”后⾯写条件,可以传⼊Null或Empty。调⽤的时候,可以类似如:and =@beginPrice /// 当前页的页码,最⼩值应该为1 /// 每页显⽰的记录数,最⼩值应该为1 /// SQL语句 { if (OrEmpty(tableName)) { throw new ArgumentNullException("tableName", (tCulture, _NullOrEmpty)); } if (OrEmpty(orderColumnNameAndAscOrDesc)) { throw new ArgumentNullException("orderColumnNameAndAscOrDesc", (tCulture, _NullOrEmpty)); } if (OrEmpty(columnNameItems)) { columnNameItems = "a.*"; } if (pageNumber < 1) { pageNumber = 1; } if (pageSize < 1) { pageSize = 1; } int beginNumber = (pageNumber - 1) * pageSize + 1; int endNumber = pageNumber * pageSize; string sqlPagerCount = ("select @__returnCount=COUNT(*) from {0} as a {1} where 1=1 {2};",tableName, joinOtherTable, whereSql); return sqlPager + sqlPagerCount; } /// 连接其他的表,可以传⼊Null或Empty。调⽤的时候,可以类似如:inner join departInfo as b on InfoId= /// 排序的列名以及Asc或Desc,即在“order by”后⾯写排序项,不能为Null和Empty。⽐如“Id asc, name desc internal static string GetPagerTSql(string columnNameItems, string tableName, string joinOtherTable, string whereSql, string orderColumnNameAndAscOrDesc string sqlPager = ("select * from (select row_number() over(order by {1}) as __MyNewId, {0} from {2} as a {3} where 1=1 {4}) as __MyTempTabl例⼆:登录错误次数限制及参数化传递防⽌SQL注⼊using System;using c;using entModel;using ;using g;using ;using ;using ;using uration;using ent;namespace 复习登录{ public partial class login : Form { public login() { InitializeComponent(); InitializeComponent(); } string str = tionStrings["sqlserver2008"].ConnectionString; DateTime dt1; private void btn_login_Click(object sender, EventArgs e) { using(SqlConnection cnn=new SqlConnection(str)) { using (SqlCommand cmd=Command()) { dText = "select * from T_User where username=@username"; hValue("@username", txt_); (); using (SqlDataReader reader = eReader()) { if (()) { int Error = 32(reader["Error"].ToString()); if (Error >= 3) { string sqltime = reader["Errortime"].ToString(); dt1 = (sqltime); DateTime dt2 = ; TimeSpan ts = dt2 - dt1; if (inutes < 5) { ("对不起,你已经输⼊3次连续错误密码,系统已经将账户冻结,请在五分钟后再试"); return; } else { clearerror(); } } string sqlpassword = reader["Password"].ToString(); if (sqlpassword == txt_) { clearerror(); if (txt_r() == "ADMIN") { (); main m = new main(); (); } else { ("登录成功"); } } else { ("密码错误"); adderror(); } } else { ("⽤户名不存在"); }
} } } } } private void adderror() { dt1 = ; using (SqlConnection cnn=new SqlConnection(str)) { using (SqlCommand cmd=Command()) { (); dText = "update T_User set Error=Error+1,Errortime=@Errortime where username=@username"; hValue("@Errortime", dt1); hValue("@username", txt_); eNonQuery(); } } } private void clearerror() { using (SqlConnection cnn=new SqlConnection(str)) { using (SqlCommand cmd=Command()) { (); dText = "update T_User set Error=0 where username=@username"; (new SqlParameter("username", txt_)); eNonQuery(); } } } }}原⽹址:1、2、
2023年6月21日发(作者:)
SQL参数化查询SQL参数化查询⼀、以往的防御⽅式以前对付这种漏洞的⽅式主要有三种:字符串检测:限定内容只能由英⽂、数字等常规字符,如果检查到⽤户输⼊有特殊字符,直接拒绝。但缺点是,系统 中不可避免地会有些内容包含特殊字符,这时候总不能拒绝⼊库。字符串替换:把危险字符替换成其他字符,缺点是危险字符可能有很多,⼀⼀枚举替换相当⿇烦,也可能有漏⽹之 鱼。存储过程:把参数传到存储过程进⾏处理,但并不是所有数据库都⽀持存储过程。如果存储过程中执⾏的命令也是通 过拼接字符串出来的,还是会有漏洞。⼆、什么是参数化查询? ⼀个简单理解参数化查询的⽅式是把它看做只是⼀个T-SQL查询,它接受控制这个查询返回什么的参数。通过使⽤不同的参数,⼀个参数化查询返回不同的结果。要获得⼀个参数化查询,你需要以⼀种特定的⽅式来编写你的代码,或它需要满⾜⼀组特定的标准。 有两种不同的⽅式来创建参数化查询。第⼀个⽅式是让查询优化器⾃动地参数化你的查询。另⼀个⽅式是通过以⼀个特定⽅式来编写你的T-SQL代码,并将它传递给sp_executesql系统存储过程,从⽽编程⼀个参数化查询。
这样的解释还是有点模糊,先看⼀例:例⼀:参数化查询 参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填⼊数值或数据的地⽅,使⽤参数 (Parameter) 来给值。 在使⽤参数化查询的情况下,数据库服务器不会将参数的内容视为SQL指令的⼀部份来处理,⽽是在数据库完成SQL指令的编译后,才套⽤参数运⾏,因此就算参数中含有指令,也不会被数据库运⾏。Access、SQL Server、MySQL、SQLite等常⽤数据库都⽀持参数化查询。//在程序中使⽤参数化查询//环境下的查询化查询也是通过Connection对象和Command对象完成。如果数据库是SQL Server,就可以⽤有名字的参数了,格式是“@”字符加上参数名。SqlConnection conn = new SqlConnection("server=(local)SQL2005;user id=sa;pwd=12345;initial catalog=TestDb");();SqlCommand cmd = new SqlCommand(“SELECT TOP 1 * FROM [User] WHERE UserName = @UserName AND Password = @Password“);tion = conn;hValue(”UserName”, “user01″);hValue(”Password”, “123456″);SqlDataReader reader = eReader();();int userId = 32(0);();();参数化查询被喻为最有效防⽌SQL注⼊的⽅法,那么存储过程⼀定是参数化过后的吗?
如果存储过得利⽤传递进来的参数,再次进⾏动态SQL拼接,这样还算做是参数化过后的吗?如果存储过程⼀定是参数化过后的,那么是不是意味着,只要使⽤存储过程就具有参数化查询的全部优点了?
如下存储过程: create procedure pro_getCustomers( @whereSql nvarchar(max))asdeclare @sql nvarchar(max)set @sql=N'select * from er ' + @whereSqlexec(@sql)Go--如果我要在中参数化查询这个存储过程,以防⽌SQL注⼊,我该怎么办呢?⽐如:exec pro_getCustomers 'where Name=@name'这种⽅法没有办法防⽌注⼊,你能做的就是对字符串进⾏过滤.拼接SQL是:
"select * from customer where 1=1" + " and name=@name" + " and sex=@sex"也就是判断参数化查询。只不过是动态地组装查询限制条件。动态拼接SQL,⽽且是参数化查询的SQL语句是没有问题的。中被SQL注⼊的问题,必须过于关键字。原作者的测试代码如下:
USE [B2CShop]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER procedure [dbo].[pro_getCustomers]( @whereSql nvarchar(max), @paramNameList nvarchar(max), @paramValueList nvarchar(max))asdeclare @sql nvarchar(max)set @sql=N'select * from er ' + @whereSqlexec sp_executesql @sql, @paramNameList , @paramValueListgo/// /// 动态执⾏存储过程 /// /// 要查询的姓名的关键字 /// 实体集合 public static List 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 eProc("proc_GetCustomerPagerBySearch",values); }/// /// 从搜索类⾥⾯拼接参数化的SQL字符串 /// /// 搜索类 /// 搜索的参数,不能传⼊Null /// 安全的SQL语句 private static string GetSafeSqlBySearchItem(CustomerSearch search, ref List sqlParams) { StringBuilder safeSqlAppend = new StringBuilder(); if (search != null) { if (!OrEmpty(uals)) { (" and Name=@nameEquals"); (new SqlParameter("@nameEquals", uals)); } if (!OrEmpty(ntains)) { (" and Name like @nameContains"); (new SqlParameter("@nameContains", "%" + ntains + "%")); } } return ng(); }/// /// 得到分页⽤的SQL语句 /// /// 要查询的列名,多个列名⽤逗号分隔。传⼊Empty或Null时,则默认查询出所有的列 /// 表名,不能为Null和Empty,默认的SQL别名为a /// 搜索条件,即在“where 1=1 ”后⾯写条件,可以传⼊Null或Empty。调⽤的时候,可以类似如:and =@beginPrice /// 当前页的页码,最⼩值应该为1 /// 每页显⽰的记录数,最⼩值应该为1 /// SQL语句 { if (OrEmpty(tableName)) { throw new ArgumentNullException("tableName", (tCulture, _NullOrEmpty)); } if (OrEmpty(orderColumnNameAndAscOrDesc)) { throw new ArgumentNullException("orderColumnNameAndAscOrDesc", (tCulture, _NullOrEmpty)); } if (OrEmpty(columnNameItems)) { columnNameItems = "a.*"; } if (pageNumber < 1) { pageNumber = 1; } if (pageSize < 1) { pageSize = 1; } int beginNumber = (pageNumber - 1) * pageSize + 1; int endNumber = pageNumber * pageSize; string sqlPagerCount = ("select @__returnCount=COUNT(*) from {0} as a {1} where 1=1 {2};",tableName, joinOtherTable, whereSql); return sqlPager + sqlPagerCount; } /// 连接其他的表,可以传⼊Null或Empty。调⽤的时候,可以类似如:inner join departInfo as b on InfoId= /// 排序的列名以及Asc或Desc,即在“order by”后⾯写排序项,不能为Null和Empty。⽐如“Id asc, name desc internal static string GetPagerTSql(string columnNameItems, string tableName, string joinOtherTable, string whereSql, string orderColumnNameAndAscOrDesc string sqlPager = ("select * from (select row_number() over(order by {1}) as __MyNewId, {0} from {2} as a {3} where 1=1 {4}) as __MyTempTabl例⼆:登录错误次数限制及参数化传递防⽌SQL注⼊using System;using c;using entModel;using ;using g;using ;using ;using ;using uration;using ent;namespace 复习登录{ public partial class login : Form { public login() { InitializeComponent(); InitializeComponent(); } string str = tionStrings["sqlserver2008"].ConnectionString; DateTime dt1; private void btn_login_Click(object sender, EventArgs e) { using(SqlConnection cnn=new SqlConnection(str)) { using (SqlCommand cmd=Command()) { dText = "select * from T_User where username=@username"; hValue("@username", txt_); (); using (SqlDataReader reader = eReader()) { if (()) { int Error = 32(reader["Error"].ToString()); if (Error >= 3) { string sqltime = reader["Errortime"].ToString(); dt1 = (sqltime); DateTime dt2 = ; TimeSpan ts = dt2 - dt1; if (inutes < 5) { ("对不起,你已经输⼊3次连续错误密码,系统已经将账户冻结,请在五分钟后再试"); return; } else { clearerror(); } } string sqlpassword = reader["Password"].ToString(); if (sqlpassword == txt_) { clearerror(); if (txt_r() == "ADMIN") { (); main m = new main(); (); } else { ("登录成功"); } } else { ("密码错误"); adderror(); } } else { ("⽤户名不存在"); }
} } } } } private void adderror() { dt1 = ; using (SqlConnection cnn=new SqlConnection(str)) { using (SqlCommand cmd=Command()) { (); dText = "update T_User set Error=Error+1,Errortime=@Errortime where username=@username"; hValue("@Errortime", dt1); hValue("@username", txt_); eNonQuery(); } } } private void clearerror() { using (SqlConnection cnn=new SqlConnection(str)) { using (SqlCommand cmd=Command()) { (); dText = "update T_User set Error=0 where username=@username"; (new SqlParameter("username", txt_)); eNonQuery(); } } } }}原⽹址:1、2、
发布评论