1

我刚刚意识到允许在一次调用中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 注入攻击在这里不是问题。我确实想授予用户对数据库的完全访问权限(如果他愿意,他甚至可以删除整个数据库)。

4

2 回答 2

1

如果您想要解决此类问题的通用解决方案,则答案始终是“解析格式 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 匹配。

于 2013-09-06T20:30:57.040 回答
0

允许用户进行任意查询(甚至是单个查询)会使您的程序面临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语句采用两个参数。第二个参数应该是一个序列,并且数据库驱动程序(例如 psycopg2)将用由args.

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

于 2013-09-06T20:11:02.157 回答