Python 如何使用“SELECT*FROM{table_name}”避免SQL注入?
在Python中使用Psycopg2和以下代码:Python 如何使用“SELECT*FROM{table_name}”避免SQL注入?,python,sql,postgresql,sql-injection,psycopg2,Python,Sql,Postgresql,Sql Injection,Psycopg2,在Python中使用Psycopg2和以下代码: import psycopg2 import getpass conn = psycopg2.connect("dbname=mydb user=%s" % getpass.getuser()) cursor = conn.cursor() tables = ["user", "group", "partner", "product"] for table in tables: # with sql injection c
import psycopg2
import getpass
conn = psycopg2.connect("dbname=mydb user=%s" % getpass.getuser())
cursor = conn.cursor()
tables = ["user", "group", "partner", "product"]
for table in tables:
# with sql injection
cursor.execute("SELECT name FROM %s LIMIT 1" % (table,))
print "table", table, "result", len(cursor.fetchone())
# without sql injection
cursor.execute("SELECT name FROM %s LIMIT 1", (table,))
print "table", table, "result", len(cursor.fetchone())
结果是:
table res_partner result 1
Traceback (most recent call last):
File "my_psycopg2_example.py", line 16, in <module>
cursor.execute("SELECT name FROM %s LIMIT 1", (table,))
psycopg2.ProgrammingError: syntax error at or near "'res_partner'"
LINE 1: SELECT name FROM 'res_partner' LIMIT 1
使用SQL注入,它可以正常工作
但我们不想制造安全问题
我们在阅读中发现了以下评论:
只能通过此方法绑定变量值:它不应用于设置表名或字段名。对于这些元素,在运行execute之前应使用普通字符串格式
但是如果我们使用普通的字符串格式,我们也会有SQL注入
管理这种特殊情况并避免SQL注入的好方法是什么?我认为您混淆了SQL注入的定义。SQL注入是对您的软件的一种攻击,其中有人导致您的SQL查询执行您不希望执行的操作。字符串插值不是SQL注入。字符串插值有时可以启用SQL注入,但并不总是如此。要了解字符串插值并不总是不安全的,请考虑以下哪项最安全: sql='从用户中选择名称' sql='从'+'用户'中选择名称' sql='从%s%['用户']中选择名称 sql='从{}中选择名称'。格式为'user' 这些代码行中的每一行都做着完全相同的事情,因此没有一行代码比其他代码更安全。在您的示例中,没有SQL注入的危险,因为您只是构建一个硬编码的SQL查询字符串 另一方面,如果表值来自用户,则可能存在安全问题: 如果他们传递了一个已存在的表的名称,但您不希望他们查询,该怎么办
table = 'secrets'
sql = 'SELECT name FROM %s LIMIT 1' % table
结果:
SELECT name FROM secrets LIMIT 1
SELECT name FROM product;
DROP TABLE user;
-- LIMIT 1
如果他们传递的不是实际的表名呢
table = 'product; DROP TABLE user; --'
sql = 'SELECT name FROM %s LIMIT 1' % table
结果:
SELECT name FROM secrets LIMIT 1
SELECT name FROM product;
DROP TABLE user;
-- LIMIT 1
您可以通过检查是否允许使用表名来防止这种情况:
if table.lower() not in ["user", "group", "partner", "product"]:
raise Something('Bad table name: %r' % table)
在execute函数中使用psycopg2查询参数是最安全的,当参数用作文本时,使用起来也很方便
cursor.mogrify("select * from foo where bar = %s", ('example',))
# yields "select * from foo where bar = 'example'"
请注意,cursor.mogrify的行为类似于execute,但只是显示格式化的SQL,而没有实际执行它
然而,当您希望参数是表、模式或其他标识符时,这就有点棘手了。您可以使用AsIs包装参数,但这仍然为SQL注入打开了大门
from psycopg2.extensions import AsIs
cur.mogrify('select %s from foo;', (AsIs('* from dual; drop table students; --'),))
# yields 'select * from dual; drop table students; -- from foo;'
看起来psycopg2>=2.7的新开发将有一个标识符类,您可以将参数封装在其中,希望是安全的。如果它还没有发布,或者您还没有,这里有一种方法可以创建您自己的类。我将在下面给出一些片段,但您也可以看到
如果您已经有一个psycopg2游标实例,可以通过以下方式测试/使用它:
# Test that a valid identifier formats into string
cursor.mogrify('select %s from foo;', (QuotedIdentifier('bar'),))
# returns 'select bar from foo;'
# Test formatting both an identifier and a literal
cursor.mogrify(
'select * from foo where %s = %s;',
(
QuotedIdentifier('bar'),
'example'
)
)
# returns "select * from foo where bar = 'example';"
# Test that a non-valid identifier fails with exception
cursor.mogrify('select %s from foo;', (QuotedIdentifier('* from dummy; drop table students; --'),))
"""Returns following:
---------------------------------------------------------------------------
NotSqlIdentifierError Traceback (most recent call last)
<ipython-input-14-d6a960dc458a> in <module>()
----> 1 cur.mogrify('select %s from foo;', (QuotedIdentifier('* from dummy; drop table students; --'),))
<ipython-input-12-0a1327cbaf78> in getquoted(self)
18 return self.obj_str
19 else:
---> 20 raise NotSqlIdentifierError(repr(self.obj_str))
21
22 psycopg2.extensions.register_adapter(QuotedIdentifier, lambda x: x)
NotSqlIdentifierError: '* from dummy; drop table students; --'
"""
有关包装SQL参数的自定义类机制的更多信息,请参阅文档中的。我认为您的代码可能缺少;。您的一行代码可能如下所示:
cursor.execute('SELECT name FROM %s LIMIT 1;', (table,))
如果所有其他操作都失败,您可以为该输入设置一个过滤器,以去除控制字符。对我来说听起来很痛苦,但你要有能力。然后,您的代码将类似于:
cursor.execute('SELECT name FROM %s LIMIT 1;' % (functionToSanitizeInput(table)))
也许您可以使用此项目:
================================================
非常好的参考:
====================================================这是一篇关于注入和python代码的好文章 你可以改变
tables = ["user", "group", "partner", "product"]
for table in tables:
cursor.execute("SELECT name FROM %s LIMIT 1" % (table,))
到
@moylop260这是您的真实示例,还是表来自真实代码中的其他地方?因为在本例中,表来自代码中的一个常量,所以不存在安全问题。