简体   繁体   English

Python / SQLite3在WHERE-Clause中转义

[英]Python/SQLite3 escaping in WHERE-Clause

How should i do real escaping in Python for SQLite3? 我应该如何在Python中为SQLite3进行真正的转义?

If i google for it (or search stackoverflow) there are tons of questions for this and every time the response is something like: 如果我谷歌(或搜索stackoverflow)有很多问题,每次响应是这样的:

dbcursor.execute("SELECT * FROM `foo` WHERE `bar` like ?", ["foobar"])

This helps against SQL-Injections, and is enough if i would do just comperations with "=" but it doesn't stripe Wildcards of course. 这有助于防止SQL注入,并且如果我只是与“=”进行竞争就足够了,但它当然不会对通配符进行条带化处理。

So if i do 所以,如果我这样做

cursor.execute(u"UPDATE `cookies` set `count`=? WHERE `nickname` ilike ?", (cookies, name))

some user could supply "%" for a nickname and would replace all of the cookie-entries with one line. 某些用户可以为昵称提供“%”,并用一行替换所有cookie条目。 I could filter it myself (ugh… i probably will forget one of those lesser-known wildcards anyway), i could use lowercase on nick and nickname and replace "ilike" with "=", but what i would really like to do would be something along the lines of: 我可以自己过滤它(呃...我可能会忘记其中一个鲜为人知的通配符),我可以在昵称和昵称上使用小写,并用“=”替换“ilike”,但我真正想做的是类似的东西:

foo = sqlescape(nick)+"%"
cursor.execute(u"UPDATE `cookies` set `count`=? WHERE `nickname` ilike ?", (cookies, foo))

? parameters are intended to avoid formatting problems for SQL strings (and other problematic data types like floating-point numbers and blobs). 参数旨在避免SQL字符串(以及其他有问题的数据类型,如浮点数和blob)的格式化问题。

LIKE/GLOB wildcards work on a different level; LIKE / GLOB通配符在不同的层面上工作; they are always part of the string itself. 它们总是字符串本身的一部分。 SQL allows to escape them, but there is no default escape character; SQL允许转义它们,但是没有默认的转义字符; you have to choose some with the ESCAPE clause : 你必须使用ESCAPE子句选择一些:

escaped_foo = my_like_escape(foo, "\\")
c.execute("UPDATE cookies SET count = ? WHERE nickname LIKE ? ESCAPE '\',
          (cookies, escaped_foo))

(And you have to write your own my_like_escape function for % and _ (LIKE) or * and ? (GLOB).) (你必须为%_ (LIKE)或*? (GLOB)编写自己的my_like_escape函数。)

You've avoided outright code injection by using parametrized queries. 您通过使用参数化查询避免了直接代码注入。 Now it seems you're trying to do a pattern match with user-supplied data, but you want the user-supplied portion of the data to be treated as literal data (hence no wildcards). 现在,您似乎正在尝试与用户提供的数据进行模式匹配,但您希望将用户提供的数据部分视为文字数据(因此没有通配符)。 You have several options: 你有几个选择:

  1. Just filter the input. 只需过滤输入。 SQLite's LIKE only understands % and _ as wildcards, so it's pretty hard to get it wrong. SQLite的LIKE只能理解 %_作为通配符,因此很难弄错。 Just make sure to always filter inputs. 只需确保始终过滤输入。 (My preferred method: Filter just before the query is constructed, not when user input is read). (我的首选方法:在构造查询之前过滤,而不是在读取用户输入时过滤)。

  2. In general, a "whitelist" approach is considered safer and easier than removing specific dangerous characters. 通常,“白名单”方法被认为比删除特定危险字符更安全,更容易。 That is, instead of deleting % and _ from your string (and any "lesser-known wildcards", as you say), scan your string and keep only the characters you want. 也就是说,不要删除字符串中的%_ (以及任何“鲜为人知的通配符”,如你所说),扫描你的字符串并只保留你想要的字符。 Eg, if your "nicknames" can contain ASCII letters, digits, "-" and ".", it can be sanitized like this: 例如,如果你的“昵称”可以包含ASCII字母,数字,“ - ”和“。”,它可以像这样清理:

     name = re.sub(r"[^A-Za-z\\d.-]", "", name) 

    This solution is specific to the particula field you are matching against, and works well for key fields and other identifiers. 此解决方案特定于您要匹配的特定字段,并且适用于关键字段和其他标识符。 I would definitely do it this way if I had to search with RLIKE , which accepts full regular expressions so there are a lot more characters to watch out for. 如果我必须使用RLIKE进行搜索,我肯定会这样做,它接受完整的正则表达式,因此需要注意更多的字符。

  3. If you don't want the user to be able to supply a wildcard, why would you use LIKE in your query anyway? 如果您不希望用户能够提供通配符,为什么还要在查询中使用LIKE If the inputs to your queries come from many places in the code (or maybe you're even writing a library), you'll make your query safer if you can avoid LIKE altogether: 如果您的查询的输入来自代码中的许多位置(或者您甚至可能正在编写库),那么如果您完全避免使用LIKE则会使查询更安全:

    • Here's case insensitive matching : 这是不区分大小写的匹配

       SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE 
    • In your example you use prefix matching (" sqlescape(nick)+"%" "). 在您的示例中,您使用前缀匹配(“ sqlescape(nick)+"%" ”)。 Here's how to do it with exact search: 以下是使用精确搜索的方法:

       size = len(nick) cursor.execute(u"UPDATE `cookies` set `count`=? WHERE substr(`nickname`, 1, ?) = ?", (cookies, size, nick)) 

Ummm, normally you'd want just replace 'ilike' with normal '=' comparison that doesn't interpret '%' in any special way. 嗯,通常你只想用正常的'='替换'ilike',而不是以任何特殊的方式解释'%'。 Escaping (effectively blacklisting of bad patterns) is error prone, eg even if you manage to escape all known patterns in the version of sqlLite you use, any future upgrade can put you at risk, etc.. 转义(有效地将坏模式列入黑名单)是容易出错的,例如,即使您设法逃避您使用的sqlLite版本中的所有已知模式,任何未来的升级都可能使您处于危险之中等等。

It's not clear to me why you'd want to mass-update cookies based on a fuzzy match on user name. 我不清楚为什么你想根据用户名的模糊匹配来批量更新cookie。

If you really want to do that, my preferred approach would be to SELECT the list first and decide what to UPDATE at the application level to maintain a maximum level of control. 如果你真的想这样做,我首选的方法是首先选择列表并决定在应用程序级别更新什么以保持最高控制级别。

There are several very fun ways to do this with string format -ing. 使用字符串format有几种非常有趣的方法。

From Python's Documentation : 从Python的文档

The built-in str and unicode classes provide the ability to do complex variable substitutions and value formatting via the str.format() method: 内置的strunicode类提供了通过str.format()方法执行复杂变量替换和值格式化str.format()

s = "string"
c = "Cool"
print "This is a {0}. {1}, huh?".format(s,c)
#=> This is a string. Cool, huh? 

Other nifty tricks you can do with string formatting: 你可以用字符串格式化做的其他漂亮的技巧:

"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'.`

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM