我有以下代码,它是完全安全的,用户无法输入任何可能导致 SQL 注入(inject)的内容。
value = get_value_from_user_input()
query = \
"""
SELECT *
FROM TestTable
WHERE CompareValue = ?
"""
cursor.execute(query, value)
但是如果我需要从用户那里获取表名怎么办?我不能这样做,因为这会让我容易受到 SQL 注入(inject)的攻击;
table_name = get_table_name_from_user_input()
query = \
f"""
SELECT *
FROM {table_name}
WHERE CompareValue = 1234
"""
cursor.execute(query)
而且我也不能这样做(在我看来这是合乎逻辑的),因为它会给出错误。
table_name = get_table_name_from_user_input()
query = \
"""
SELECT *
FROM ?
WHERE CompareValue = 1234
"""
cursor.execute(query, table_name)
那么我应该怎么做呢?
额外信息:
该程序供我公司内部使用,该软件的所有用户都拥有数据库的管理员权限。不允许他们注入(inject) SQL 可能有点毫无意义,因为他们无论如何都可以执行原始 SQL 查询。我只是想迂腐一点,不允许我的程序执行随机 SQL。 get_table_name_from_user_input() 实际上是从描述数据库结构的配置文件中获取表名,因此如果有一天数据库中的表名发生更改,用户可以轻松编辑它,而无需触摸源代码。
最佳答案
您可以使用 pyodbc 的 Cursor#tables 直接从数据库获取有效表名的列表。功能:
crsr = cnxn.cursor()
table_names = [x[2] for x in crsr.tables(tableType='TABLE')]
print(table_names) # ['customer', 'invoice', ...]
正如您所指出的,您不能使用参数为查询提供对象(例如表或列)名称,但您可以使用 T-SQL QUOTENAME函数来帮助确保您构建的(动态)SQL 有效。
您留意可能的 SQL 注入(inject)问题是件好事,但并非所有动态 SQL 都是邪恶的。有时,就像在本例中一样,这是必要的;您只需要采取适当的措施来保护自己。
关于python - 如何从pyodbc中的用户获取表名,同时避免SQL注入(inject)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55853739/