Python/SQLite3在WHERE子句中转义

Python/SQLite3在WHERE子句中转义,python,sql,sqlite,escaping,sql-like,Python,Sql,Sqlite,Escaping,Sql Like,我应该如何在Python中为SQLite3执行真正的转义 如果我在谷歌上搜索(或搜索stackoverflow),会有很多问题,每次的回答都是这样的: dbcursor.execute("SELECT * FROM `foo` WHERE `bar` like ?", ["foobar"]) 这有助于防止SQL注入,如果我只使用“=”进行比较就足够了,但它当然不会带通配符 所以如果我这样做了 cursor.execute(u"UPDATE `cookies` set `count`=? WHE

我应该如何在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条目替换为一行。 我可以自己过滤它(呃……我可能会忘记其中一个鲜为人知的通配符),我可以在nick和昵称上使用小写字母,并将“ilike”替换为“=”,但我真正想做的是:

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

参数旨在避免SQL字符串(以及其他有问题的数据类型,如浮点数和BLOB)的格式问题

LIKE/GLOB通配符在不同级别上工作;它们总是字符串本身的一部分。 SQL允许转义它们,但没有默认的转义字符;您必须使用以下选项选择一些:


(你必须为
%
(like)或
*
(GLOB)编写自己的
my\u like\u escape
函数)。

使用字符串
格式
-ing有几种非常有趣的方法

来自Python的:

内置的
str
unicode
类提供了通过
str.format()
方法进行复杂变量替换和值格式化的能力:

使用字符串格式可以实现的其他漂亮技巧:

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

嗯,通常您只需要将“ilike”替换为正常的“=”比较,该比较不会以任何特殊方式解释“%”。转义(有效地将坏模式列入黑名单)容易出错,例如,即使您设法转义您使用的sqlLite版本中的所有已知模式,将来的任何升级都可能使您面临风险,等等

我不清楚你为什么要根据用户名上的模糊匹配来批量更新cookies


如果您真的想这样做,我的首选方法是首先选择列表,然后决定在应用程序级别更新什么以保持最大的控制级别。

通过使用参数化查询避免了直接的代码注入。现在,您似乎正在尝试使用用户提供的数据进行模式匹配,但希望将用户提供的数据部分视为文字数据(因此没有通配符)。您有几个选择:

  • 只需过滤输入。SQLite的
    类似于
    %
    \
    作为通配符,因此很难出错。只需确保始终过滤输入。(我的首选方法是:在构造查询之前过滤,而不是在读取用户输入时过滤)

  • 一般来说,“白名单”方法被认为比删除特定的危险字符更安全、更容易。也就是说,不要从字符串(以及您所说的任何“鲜为人知的通配符”)中删除
    %
    ,而是扫描字符串并只保留所需的字符。例如,如果您的“昵称”可以包含ASCII字母、数字、“-”和“.”,则可以按如下方式对其进行清理:

    name = re.sub(r"[^A-Za-z\d.-]", "", name)
    
    此解决方案特定于要匹配的特定字段,适用于关键字段和其他标识符。如果我必须使用
    RLIKE
    进行搜索,我肯定会这样做,因为它接受完整的正则表达式,所以有更多的字符需要注意

  • 如果您不希望用户能够提供通配符,那么为什么还要在查询中使用
    LIKE
    ?如果查询的输入来自代码中的许多地方(或者您甚至正在编写一个库),如果您可以避免像这样的
    ,那么您的查询将更加安全:

    • 这是:

    • 在您的示例中,使用前缀匹配(“
      sqlescape(nick)+”%“
      ”)。以下是使用精确搜索的方法:

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

  • 除了模糊搜索之外,为什么还要使用LIKE?可以忽略搜索本身吗?我不是这么问的。(或者更确切地说:我可以用它进行模糊搜索,一点也不会改变这个问题,不是吗?)好吧……这就是如何做到的,它很有帮助,但让每个用户都有编写自己的转义函数的问题。所以我认为没有更好的解决方案:-/很容易出错,并且可以自己为每个人工作。几乎是我最糟糕的情况。我知道如何格式化字符串。。。但这无助于找到所有需要逃避的事情(不仅仅是我能记得的那两件事)-不是我想要的。。。完全是 啊因为这是解决我问题的办法。等待它不是!
    name = re.sub(r"[^A-Za-z\d.-]", "", name)
    
    SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
    
    size = len(nick)
    cursor.execute(u"UPDATE `cookies` set `count`=? WHERE substr(`nickname`, 1, ?) = ?", 
                    (cookies, size, nick))