参数化查询如何帮助防止 SQL 注入?

Posted

技术标签:

【中文标题】参数化查询如何帮助防止 SQL 注入?【英文标题】:How do parameterized queries help against SQL injection? 【发布时间】:2011-03-29 05:50:33 【问题描述】:

在查询 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 转换为整数

    int tagnumber = txtTagNumber.Text.ToInt16(); /* EDITED */
    INSERT into Cars values(tagnumber.Text); /* then is it the same? */
    

另外,这里我会使用正则表达式验证来阻止非法字符的插入。

【问题讨论】:

既然参数化查询已经解决了问题,为什么还要编写自己的验证? Plus 验证很难从文本输入中解放出来,并且参数化查询会进行适当的“转义”(客户端实际上将数据直接传递给服务器)。 @dvhh:你能举个ESCAPING的例子吗? 一个!无效!转义例如 String.replace("'","''")。参数化执行命令使用另一种你几乎不需要知道的编码形式 Why do we always prefer using parameters in SQL statements?的可能重复 【参考方案1】:

参数化查询在运行 SQL 查询之前会正确替换参数。它完全消除了“脏”输入改变查询含义的可能性。也就是说,如果输入包含 SQL,它就不能成为执行内容的一部分,因为 SQL 从未注入到结果语句中。

【讨论】:

@sqlchild:表、列或过程名称(通常是井标识符)不能参数化,只能参数化“值”或“参数”(如 OJ 所说)。就像在 WHERE 或 JOIN/IN 子句、函数或过程的参数等中使用的一样。因此名称为“参数化”查询。 @Christian.K :先生,我的意思是,如果我将文本框中的 DROP 表 Cars 传递给插入语句,则插入汽车值(@carname); cmd.Parameters.Add(@carname,SQLDBTYPE varchar),那么参数化查询会做什么? @sqlchild:抱歉,误会了。它只会在相应的列/字段中插入字符串“DROP table Cars”。 @sqlchild:不,它不被称为转义。它是数据库提供的一种机制,使转义变得不必要。我认为您需要阅读更多有关 SQL 注入的背景知识(例如,此处为 en.wikipedia.org/wiki/SQL_injection) - 不仅仅适合评论或回复您的原始问题。 @sqlchild:您开始改变话题并且已经接受了您原来问题的答案。请考虑发布关于您需要了解的其他事项的另一个/新问题。这就是 SO 的工作方式,它可以更好地被发现并造福于所有人。【参考方案2】:

想象一个动态 SQL 查询

sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND
Pass=' + password

所以一个简单的 sql 注入只需将用户名输入为' OR 1=1-- 这将有效地进行 sql 查询:

sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS='
+ password

这表示选择用户名为空白 ('') 的所有客户或 1=1,这是一个布尔值,等于真。然后它使用 -- 来评论 查询的其余部分。所以这只会打印出所有 客户表,或者用它做任何你想做的事情,如果登录,它 将使用第一个用户的权限登录,这通常是 管理员。

现在参数化查询的做法有所不同,代码如下:

sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'

parameters.add("User", username) parameters.add("Pass", password)

其中用户名和密码是指向关联的变量 输入用户名和密码

此时,您可能会想,这并不会改变任何事情 一点也不。当然,您仍然可以只输入用户名字段 像Nobody OR 1=1'--,有效地进行查询:

sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND
Pass=?'

这似乎是一个有效的论点。但是,你错了。

参数化查询的工作方式是 sqlQuery 作为 查询,并且数据库确切地知道该查询将做什么,并且 只有这样,它才会仅将用户名和密码作为值插入。 这意味着他们不能影响查询,因为数据库已经 知道查询会做什么。所以在这种情况下,它会寻找一个 "Nobody OR 1=1'--" 的用户名和一个空白密码,应该是 错误。

Source: lavamunky.com; Nov 2011

【讨论】:

【参考方案3】:

当一个可能的参数中包含 sql 并且字符串没有按应有的方式处理时,就会发生 sql 注入

例如:

var sqlquerywithoutcommand = "select * from mytable where rowname =  '" + condition+''";

并且条件是来自请求中的用户的字符串。如果条件是恶意的 比如说:

var sqlquerywithoutcommand = "select * from mytable where rowname =  '" + "a' ;drop table  mytable where '1=1"+"'";

您最终可能会运行恶意脚本。

但是使用参数输入将被清除任何可能转义字符串字符的字符...

你可以确保无论什么进来它都不能运行注入脚本。

使用带有参数的命令对象实际执行的sql看起来像这样

select * from mytable where rowname = 'a'';drop table mytable where 1=1'''

本质上,它会寻找 rowname = a';drop table mytable where 1=1' 的行 并且不运行剩余的脚本

【讨论】:

但是当sql从参数中获取值时,它是如何读取它的,我的意思是它会以与直接从字符串相同的方式获取它,即它将它读取为drop table mytable ,还是我错了? @ sqlchild :nope...它用 '' 转义所有 ' 以便整个字符串成为参数...让我将其添加到答案中【参考方案4】:

参数化查询处理一切 - 为什么要麻烦?

使用参数化查询,除了常规注入之外,您还可以处理所有数据类型、数字(int 和 float)、字符串(带有嵌入式引号)、日期和时间(.ToString() 时没有格式问题或本地化问题不会以不变的文化调用,并且您的客户会转移到具有意外日期格式的机器上)。

【讨论】:

他们肯定会处理 SQL 注入,但不会处理其他类型的注入(主要是 web 端注入,如 javascript)。【参考方案5】:

参数化查询允许客户端从查询文本中单独传递数据。 在大多数没有文本的地方,您可以进行验证+转义。 当然参数化对其他类型的注入没有帮助,但是由于参数是单独传递的,它们不能用作执行文本查询。

一个很好的类比是与大多数现代处理器和操作系统一起使用的“最近”执行位,以防止缓冲区溢出。它仍然允许缓冲区溢出,但阻止执行注入的数据。

【讨论】:

【参考方案6】:

为什么会有这样的感觉是完全可以理解的。

sqlQuery = "select * from users where username='+username+';"

sqlQuery = "select * from users where username=@username;"

上述两个查询似乎都做同样的事情,但实际上并没有。

前者使用输入来进行查询,后者决定查询,但仅在执行查询期间替换输入。

更清楚地说,参数的值位于堆栈中存储变量内存的某个位置,并在需要时用于搜索。

因此,如果我们将' OR '1'='1 作为用户名的输入,则前者将动态地构造一个新的查询或查询作为sql 查询字符串sqlQuery 的一部分,然后执行该查询。

在相同的输入上,后者将在users 表的username 字段中搜索' OR '1'=',并在查询字符串sqlQuery 中使用静态 指定查询

只是为了巩固它,这是您使用参数进行查询的方式:

SqlCommand command = new SqlCommand(sqlQuery,yourSqlConnection);

SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@username";
parameter.Value = "xyz";

command.Parameters.Add(parameter);

【讨论】:

以上是关于参数化查询如何帮助防止 SQL 注入?的主要内容,如果未能解决你的问题,请参考以下文章

这个参数化查询如何防止 SQL 注入?

防止SQL注入

为什么参数化查询可以防止SQL注入?(转)

参数化查询为什么能够防止SQL注入 (转)

参数化查询防止Sql注入

使用参数化查询防止SQL注入漏洞