C# 参数化查询如何帮助防止SQL注入?
在查询1和查询2中,文本框中的文本都被插入到数据库中。这里参数化查询的意义是什么C# 参数化查询如何帮助防止SQL注入?,c#,.net,sql,sql-server-2005,parameterized-query,C#,.net,Sql,Sql Server 2005,Parameterized Query,在查询1和查询2中,文本框中的文本都被插入到数据库中。这里参数化查询的意义是什么 将txtTagNumber作为查询参数传递 SqlCommand cmd = new SqlCommand("INSERT INTO dbo.Cars " +"VALUES(@TagNbr);" , conn); cmd.Parameters.Add("@TagNbr", SqlDbType.Int); cmd.Parameters["@TagNbr"].Value = txtTagNumber.Text; 在
txtTagNumber
作为查询参数传递
SqlCommand cmd = new SqlCommand("INSERT INTO dbo.Cars " +"VALUES(@TagNbr);" , conn);
cmd.Parameters.Add("@TagNbr", SqlDbType.Int);
cmd.Parameters["@TagNbr"].Value = txtTagNumber.Text;
txtTagNumber
转换为整数
int tagnumber = txtTagNumber.Text.ToInt16(); /* EDITED */
INSERT into Cars values(tagnumber.Text); /* then is it the same? */
此外,这里我将使用正则表达式验证来停止插入非法字符。参数化查询在运行SQL查询之前对参数进行适当的替换。它完全消除了“脏”输入改变查询含义的可能性。也就是说,如果输入包含SQL,它就不能成为执行的内容的一部分,因为SQL从未被注入到结果语句中。参数化查询处理所有问题-为什么要麻烦呢
使用参数化查询,除了常规注入外,您还可以处理所有数据类型、数字(int和float)、字符串(带嵌入引号)、日期和时间(如果未使用不变区域性调用.ToString(),并且您的客户端移动到具有意外日期格式的计算机,则不会出现格式问题或本地化问题).参数化查询允许客户端从查询文本中单独传递数据。 在最自由的文本上,您将执行验证+转义。 当然,参数化对其他类型的注入没有帮助,但由于参数是单独传递的,因此它们不能用作执行文本查询
一个很好的类比是,大多数现代处理器和操作系统都使用“最近”执行位来防止缓冲区溢出。它仍然允许缓冲区溢出,但会阻止执行注入的数据。如果可能的参数中包含sql,并且字符串未按应有方式处理,则会发生sql注入 例如: 条件是来自请求中用户的字符串。如果条件是恶意的 比如说: 您可能最终运行恶意脚本 但是使用参数输入将清除可能转义字符串的任何字符 您可以确保,无论输入什么,它都无法运行注入脚本 使用带有参数的命令对象,实际执行的sql将如下所示
select * from mytable where rowname = 'a'';drop table mytable where 1=1'''
在本质上,它将查找rowname=a'的行;删除表mytable,其中1=1'
不运行剩下的脚本是可以理解的
sqlQuery = "select * from users where username='+username+';"
vs
上述两个查询似乎做了相同的事情,但实际上没有
前者将输入用于查询,后者决定查询,但只替换查询执行期间的输入
更清楚的是,参数值位于堆栈上存储变量内存的某些位置,并在需要时用于搜索
因此,如果我们在用户名中输入'或'1'='1
,前者将动态地构造一个或多个新查询,作为sql查询字符串sqlQuery
的一部分,然后执行
在相同的输入上,后者将使用查询字符串sqlQuery
为了整合它,以下是如何使用参数进行查询:
SqlCommand command = new SqlCommand(sqlQuery,yourSqlConnection);
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@username";
parameter.Value = "xyz";
command.Parameters.Add(parameter);
想象一个动态SQL查询
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND
Pass=' + password
因此,一个简单的sql注入就是将用户名作为”或
1=1--
这将有效地使sql查询:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS='
+ password
这表示选择用户名为空(“”)或
1=1,这是一个布尔值,等于true。然后它用--来评论
完成查询的其余部分。所以这会打印出所有的
客户表,或者对其执行任何您想要的操作,如果登录,则
将使用第一个用户的权限登录,这通常是
管理员
现在,参数化查询的方式有所不同,代码如下:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
参数。添加(“用户”,用户名)参数。添加(“通过”,密码)
其中username和password是指向关联
输入用户名和密码
现在,你可能会想,这不会改变任何事情
完全当然,您仍然可以输入用户名字段
类似于Nobody或1=1'--,有效地进行查询:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND
Pass=?'
这似乎是一个有效的论点。但是,你错了
参数化查询的工作方式是将sqlQuery作为
数据库确切地知道这个查询将做什么,并且
只有这样,它才会将用户名和密码仅仅作为值插入。
这意味着它们无法影响查询,因为数据库已经存在
知道查询将做什么。所以在这种情况下,它会寻找一个
“无人或1=1”的用户名--“
和一个空白密码,应该是
我错了
既然参数化查询已经解决了问题,为什么还要编写自己的验证?另外,验证很难不使用文本输入,参数化查询会进行正确的“转义”(客户端实际上会将数据直接传递到服务器)。@dvhh:您能举个转义的例子吗?一个!无效的转义将是例如String.replace(“'”,“''”)。参数化执行命令使用另一种形式的编码,您几乎不需要知道可能的重复。它们肯定会处理SQL注入,但不会处理其他类型的注入(主要是web端注入,如javascript)。@sqlchild:表、列或过程名(通常是标识符)不能参数化,只能是“值”或者“论点”(正如OJ所说)。就像在WHERE或JOIN/in子句、函数或过程的参数中使用的一样,因此被称为“参数化”查询。@Christian.K:先生,我的意思是,如果我
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS='
+ password
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND
Pass=?'