SQL查询参数化如何工作?

因为我似乎是世界上唯一没有得到它的人,所以我觉得有点傻,但是无论如何, 我将以Python为例。 当我使用原始SQL查询(我通常使用ORM)时,我使用参数化,就像使用SQLite的这个例子:

方法A:

username = "wayne"
query_params = (username)
cursor.execute("SELECT * FROM mytable WHERE user=?", query_params)

我知道这是有效的,我知道这是通常推荐的方式。 SQL注入容易的方式来做同样的事情会是这样的:

方法B:

username = "wayne"
cursor.execute("SELECT * FROM mytable WHERE user='%s'" % username)

据我所知,我理解SQL注入,正如这篇维基百科文章中所解释的那样。 我的问题很简单:方法A与方法B有什么不同? 为什么方法A的最终结果与方法B不一样? 我假定cursor.execute()方法(Python的DB-API规范的一部分)负责正确地转义和类型检查输入,但这从来没有明确说明过。 这种情况下的所有参数是? 对我而言,当我们说“参数化”时,所有的意思都是“字符串替换”,就像%格式化一样。 那是不正确的?


参数化查询实际上不会执行字符串替换。 如果您使用字符串替换,那么SQL引擎实际上会看到类似的查询

SELECT * FROM mytable WHERE user='wayne'

如果你使用? 参数,那么SQL引擎会看到类似的查询

SELECT * FROM mytable WHERE user=<some value>

这意味着在它看到字符串“wayne”之前,它可以完全解析查询并且通常理解查询的功能。 它将“wayne”插入到它自己的查询表示中,而不是描述查询的SQL字符串。 因此,SQL注入是不可能的,因为我们已经通过了该进程的SQL阶段。

(以上是概括性的,但它或多或少地表达了这个想法。)


使用参数化查询是一种很好的方法,可以转移任务以避免注入DB客户端库。 它将在用“?”替换字符串之前进行转义。 这是在DB服务器之前的客户端库中完成的。

如果你有MySQL的运行,打开SQL日志,并尝试一些参数化查询,你会看到MySQL服务器正在接收完全替代的查询,没有“?” 在其中,但MySQL客户端库已经在您的“参数”中为您退出了任何引号。

如果您只使用方法B替换字符串,则不会自动转义。

与MySQL协同工作,您可以提前准备参数化查询,然后重复使用准备的语句。 当你准备一个查询时,MySQL解析它并给你一个准备好的语句 - 一些MySQL理解的解析表示。 每次使用准备好的语句时,不仅可以防止注入,还可以避免再次解析查询的成本。

而且,如果你真的想要安全,你可以修改你的数据库访问/ ORM层,以便1)Web服务器代码只能使用预先准备的语句,2)你只能在Web服务器启动前准备语句。 然后,即使您的网络应用程序被攻入(例如通过缓冲区溢出漏洞),黑客仍然只能使用准备好的语句,但仅此而已。 为此,您需要监禁您的Web应用程序,并且只允许通过您的数据库访问/ ORM层访问数据库。


当你做文本替换时(比如你的方法B),你必须警惕引号等等,因为服务器会得到一段文本,并且必须确定值的结束位置。

使用参数化语句OTOH,DB服务器按原样获取语句,而不使用参数。 该值使用简单的二进制安全协议作为不同的数据发送到服务器。 因此,你的程序不需要在值的周围加引号,当然,值本身中是否有引号也没关系。

一个类比是关于源代码和编译代码:在您的方法B中,您正在构建过程的源代码,因此您必须确保严格遵循语言语法。 使用方法A,您首先构建并编译一个过程,然后(在您的示例中紧随其后),以该值作为参数调用该过程。 当然,内存中的值不受语法限制。

呃......这不是一个真正的比喻,它实际上就是发生了什么(大致)。

链接地址: http://www.djcxy.com/p/16763.html

上一篇: How does SQL query parameterisation work?

下一篇: Idiorm pdo prepared statement