2个类似的“更新”变体的工作方式不同

时间:2011-05-26 07:49:38

标签: python sqlite

这是快照#1:

strr = "UPDATE fileinfo SET file_name = ? WHERE md5sum = ?"
cr.execute(strr, ( rec[0], rec[1]) )

和#2:

strr = "UPDATE fileinfo SET file_name = {0} WHERE md5sum = {1}".format(rec[0], rec[1])
cr.execute(strr)

第一个工作正常,而第二个工作失败。它抛出

  

sqlite3.OperationalError:无法识别   token :( rec [0]中的某个标记取决于输入数据,可能是“@”或“!”或者传递给输入的任何字符串)

python 3.2,win7

谢谢。

2 个答案:

答案 0 :(得分:1)

您展示的两个变体非常不同。在第一个中,您使用数据库api来填充参数 - 值正在被正确转义。

在第二个变体中,您只需使用pythons字符串格式将变量添加到SQL字符串中 - 值不会被转义,并且根据rec[0]rec[1]的内容,您将得到格式错误的SQL。

另请注意,这是SQL注入漏洞的路径!

答案 1 :(得分:1)

警告!接下来是错误的代码!

问题在于,在第二个示例中,您不是将字符串作为SQL字符串提供,而是作为文字值提供。这不太可能奏效!相反,你必须这样做:

strr = "UPDATE fileinfo SET file_name = '{0}' WHERE md5sum = '{1}'".format(rec[0], rec[1])
cr.execute(strr)

注意额外的单引号?这是SQL字符串语法。

但不要这样做!

问题是,如果您要替换的字符串中包含字符,SQL语法分析器将其理解为字符串上下文中的文字字符以外的任何字符。最明显的例子是'(单引号字符)本身。虽然你可能对md5sum参数足够安全,但文件名中会出现奇怪的事情(尤其是涉及非技术用户的情况!)所以最好在开始时小心。

可以通过在替换期间为值添加额外的魔法引用来处理这个问题,但它很容易出错(即使到今天仍然存在大量PHP程序中的问题)并且以错误的方式执行< / em>鉴于我们在使用准备好的声明时有更好,更简单的解决方案。

它也很慢。使用预准备语句(即,您的第一个示例)更快,因为SQL引擎可以解析代码一次而不是每次,并且可以通过有效地将它们放在右侧来处理要注入的值生成的字节码的槽。永远不需要重新计算查询计划(即,SQLite在其自身内创建的小程序),并且值本身永远不会被讨论;他们只是以正确的方式忠实地使用。