[英]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
。 *这对于接受单引号或双引号字符串的方言是正确的,它不会在另一引号类型中转义一个引号类型,并且通过将引号加倍来转义(我们将'abc''def'
错误地视为两个字符串abc
和def
,而不是一个字符串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.