我刚刚意识到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注入攻击不是问题。我确实想让用户完全访问数据库(他甚至可以删除整个数据库)。
答案 0 :(得分: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
。 *这对于接受单引号或双引号字符串的方言是正确的,不会在另一引号中转义一个引用类型,并通过加倍引用来转义引号(我们会错误地将'abc''def'
视为两个字符串abc
和def
,而不是一个字符串abc'def
,但由于我们所做的只是跳过字符串,我们得到正确的结果),但没有C风格的反斜杠逃避或其他任何事情。我相信它匹配sqlite3,因为它实际上有效,虽然不是sqlite3,因为它已记录,我不知道它是否与PostgreSQL匹配。
答案 1 :(得分:0)
允许用户进行任意查询(甚至是单个查询)可以使您的程序最多打开SQL injection attacks和denial-of-service (DOS) attacks。处理潜在恶意用户的最安全方法是精确枚举哪些查询是允许的,并且只允许用户提供参数值,而不是整个SQL查询本身。
例如,您可以定义
sql = 'INSERT INTO my_table VALUES (%s, %s)'
args = [1, 2] # <-- Supplied by the user
然后安全地执行INSERT语句:
cursor.execute(sql, args)
这称为parametrized SQL,因为sql使用%s
作为参数placemarkkers,cursor.execute
语句使用两个参数。第二个参数应该是一个序列,数据库驱动程序(例如psycopg2)将用args
提供的属性引用值替换参数placemarkkers。
这将阻止SQL注入攻击。 您仍然有责任(当您编写允许的SQL时)以防止拒绝服务攻击。例如,您可以通过确保用户提供的参数在合理范围内来尝试保护自己免受DOS攻击。