繁体   English   中英

如何在一个执行调用中避免多个查询

[英]How to avoid multiple queries in one execute call

我刚刚意识到psycopg2允许在一个execute调用中进行多个查询。

例如,此代码实际上将在my_table插入两行:

>>> import psycopg2
>>> connection = psycopg2.connection(database='testing')
>>> cursor = connection.cursor()
>>> sql = ('INSERT INTO my_table VALUES (1, 2);'
...        'INSERT INTO my_table VALUES (3, 4)')
>>> cursor.execute(sql)
>>> connection.commit()

psycopg2是否有某种禁用此功能的方法? 还是有其他方法可以防止这种情况发生?

到目前为止,我要搜索的是查询中是否有分号( ; ):

if ';' in sql:
    # Multiple queries not allowed!

但是此解决方案并不完美,因为它不允许某些有效的查询,例如:

SELECT * FROM my_table WHERE name LIKE '%;'

编辑: SQL注入攻击不是这里的问题。 我确实想授予用户对数据库的完全访问权限(如果愿意,他甚至可以删除整个数据库)。

如果您想要解决此类问题的一般解决方案,答案总是会是“解析格式X,或者至少解析得足够好以满足您的需求”。

在这种情况下,这可能非常简单。 PostgreSQL不允许在列名或表名中间使用分号,等等。 它们只能出现在字符串内部或作为语句终止符。 因此,您不需要完整的解析器,只需一个可以处理字符串的解析器即可。

不幸的是,即使那样也不是完全无关紧要的,因为您必须了解在PostgreSQL中算作字符串文字的规则。 例如, "abc\\"def"是字符串abc"def吗?

但是一旦您编写或找到可以识别PostgreSQL中字符串的解析器,就很容易:跳过所有字符串,然后查看是否还有分号。

例如(这可能不是正确的逻辑,*也是用冗长而低效的方式编写的,只是为了向您展示这个想法):

def skip_quotes(sql):
    in_1, in_2 = False, False
    for c in sql:
        if in_1:
            if c == "'":
                in_1 = False
        elif in_2:
            if c == '"':
                in_2 = False
        else:
            if c == "'":
                in_1 = True
            elif c == '"':
                in_2 = True
            else:
                yield c

然后,您可以编写:

if ';' in skip_quotes(sql):
    # Multiple queries not allowed!

如果找不到预制的解析器,首先要考虑的是:

  • 如果它是如此琐碎,以至于像find这样的简单字符串操作都可以工作,那就去做。
  • 如果这是一种简单的常规语言,请使用re
  • 如果可以描述性地解释逻辑(例如,通过BNF语法),请使用解析库或解析器生成器库,例如pyparsingpybison
  • 否则,您可能需要编写状态机,甚至是显式的迭代代码(例如上面的示例)。 但这很少是除了教学目的以外的最佳答案。

*这对于接受单引号或双引号字符串的方言是正确的,它不会在另一引号类型中转义一个引号类型,并且通过将引号加倍来转义(我们将'abc''def'错误地视为两个字符串abcdef ,而不是一个字符串abc'def ,但是由于我们要做的是无论如何都跳过字符串,所以我们得到了正确的结果),但是没有C样式的反斜杠转义符或其他任何东西。 我相信这与sqlite3实际匹配,尽管与sqlite3记录的不一致,但我不知道它是否与PostgreSQL匹配。

允许用户进行任意查询(甚至是单个查询)可以使您的程序容易受到SQL注入攻击拒绝服务(DOS)攻击 处理潜在恶意用户的最安全方法是准确枚举允许哪些查询,并且仅允许用户提供参数值,而不是整个SQL查询本身。

因此,例如,您可以定义

sql = 'INSERT INTO my_table VALUES (%s, %s)'
args = [1, 2]  # <-- Supplied by the user

然后使用以下命令安全地执行INSERT语句:

cursor.execute(sql, args)

这被称为参数化SQL,因为sql使用%s作为参数cursor.execute ,并且cursor.execute语句接受两个参数。 第二个参数应该是一个序列,并且数据库驱动程序(例如psycopg2)将用args提供的正确引用的值替换参数地标。

这将防止SQL注入攻击。 (编写允许的SQL时)仍然有责任防止拒绝服务攻击。 您可以尝试通过确保用户提供的参数在合理范围内来保护自己免受DOS攻击。

暂无
暂无

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

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