我应该如何在Python中为SQLite3进行真正的转义?
如果我用谷歌搜索(或搜索stackoverflow),会有很多问题,每次响应都是这样的:
dbcursor.execute("SELECT * FROM `foo` WHERE `bar` like ?", ["foobar"])
这有助于防止SQL注入,并且如果我只用" ="进行编译,就足够了,但它当然不会剥离通配符。
所以如果我这样做
cursor.execute(u"UPDATE `cookies` set `count`=? WHERE `nickname` ilike ?", (cookies, name))
一些用户可以提供"%"作为昵称,并用一行替换所有cookie条目。
我可以自己过滤它(嗯……我可能还是会忘记那些鲜为人知的通配符之一),我可以在昵称和昵称上使用小写字母,并用" ="替换" ilike",但是我真正想做的是 类似于以下内容:
foo = sqlescape(nick)+"%"
cursor.execute(u"UPDATE `cookies` set `count`=? WHERE `nickname` ilike ?", (cookies, foo))
为什么要使用LIKE进行除模糊搜索之外的任何操作?
您可以忽略搜索本身吗? 那不是我问的。 (或者更确切地说:我可以将其用于模糊搜索,不会稍微改变问题,对吗?)
?参数旨在避免SQL字符串(以及其他有问题的数据类型,如浮点数和blob)的格式问题。
LIKE / GLOB通配符在不同级别上起作用;它们始终是字符串本身的一部分。
SQL允许对它们进行转义,但是没有默认的转义字符。您必须使用ESCAPE子句进行选择:
escaped_foo = my_like_escape(foo,"\")
c.execute("UPDATE cookies SET count = ? WHERE nickname LIKE ? ESCAPE '\',
(cookies, escaped_foo))
(并且您必须为%和_(LIKE)或*和?(GLOB)编写自己的my_like_escape函数。)
好吧……这就是这样做的方法,虽然有很多帮助,但是却使每个用户都无法编写自己的转义功能。
所以我认为没有更好/好的解决方案:-/容易出错,并且每个人都可以自己工作。 几乎是我最坏的情况。
您已经避免使用参数化查询来进行彻底的代码注入。现在看来,您正在尝试与用户提供的数据进行模式匹配,但是您希望将用户提供的数据部分视为文字数据(因此不能使用通配符)。您有几种选择:
只需过滤输入。 SQLite的LIKE仅将%和_理解为通配符,因此很难弄错它。只要确保始终过滤输入即可。 (我的首选方法:在构造查询之前进行过滤,而不是在读取用户输入时进行过滤)。
通常,"白名单"方法被认为比删除特定的危险字符更安全,更容易。也就是说,不要从字符串(和您所说的任何"鲜为人知的通配符")中删除%和_,而是扫描字符串并仅保留所需的字符。例如,如果您的"昵称"可以包含ASCII字母,数字,"-"和"。",则可以按以下方式进行清理:
name = re.sub(r"[^A-Za-z\d.-]","", name)
该解决方案特定于您要匹配的粒子字段,并且对于关键字段和其他标识符非常有效。如果必须使用RLIKE进行搜索,我肯定会这样做,因为RLIKE接受完整的正则表达式,因此要注意的字符很多。
如果您不希望用户提供通配符,为什么无论如何仍要在查询中使用LIKE?如果查询的输入来自代码中的很多地方(或者甚至您正在编写一个库),那么如果可以完全避免使用LIKE,则可以使查询更安全:
这是不区分大小写的匹配:
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
在您的示例中,您使用前缀匹配(" sqlescape(nick)+"%"")。进行精确搜索的方法如下:
size = len(nick)
cursor.execute(u"UPDATE `cookies` set `count`=? WHERE substr(`nickname`, 1, ?) = ?",
(cookies, size, nick))
嗯,通常您只想用不以任何特殊方式解释"%"的普通" ="比较替换" ilike"。转义(有效地将不良模式列入黑名单)容易出错,例如即使您设法逃避了所使用的sqlLite版本中的所有已知模式,任何将来的升级都可能使您处于危险之中,等等。
我不清楚,为什么要基于用户名的模糊匹配来批量更新Cookie。
如果您真的想这样做,我的首选方法是先选择列表,然后决定在应用程序级别进行更新以保持最大程度的控制。
是的..因为那是我的问题的解决方案。 等等...不是!
使用字符串format -ing可以通过几种非常有趣的方法来实现。
从Python的文档中:
The built-in str and unicode classes provide the ability to do complex variable substitutions and value formatting via the str.format() method:
s ="string"
c ="Cool"
print"This is a {0}. {1}, huh?".format(s,c)
#=> This is a string. Cool, huh?
您可以使用字符串格式进行其他一些巧妙的技巧:
"First, thou shalt count to {0}".format(3) # References first positional argument
"Bring me a {}".format("shrubbery!") # Implicitly references the first positional argument
"From {} to {}".format('Africa','Mercia') # Same as"From {0} to {1}"
"My quest is {name}" # References keyword argument 'name'
"Weight in tons {0.weight}" # 'weight' attribute of first positional arg
"Units destroyed: {players[0]}" # First element of keyword argument 'players'.`
我知道如何格式化字符串...但是这对查找所有必须转义的内容(不仅是我能记住的那两件事)没有帮助-根本不是我要的...。