python - 构造具有多个 AND/OR 条件的安全 SQL 语句
问题描述
我有以下问题:我在 Python 代码中有一个非常大的字符串形式的 SQL 语句:
sql = f"""
*many statements here*
"""
该 SQL 语句的一部分是:
where 1 = 1
and selector in ('YES', 'NO')
AND parameter1 = value1
AND parameter2 = value2.1 OR parameter2 = value2.2
AND ...
其中那些 AND/OR 语句由 Python 字典以形式给出
{ parameter1: [value1], parameter2: [value2.1, value 2.2], ...}
我编写了一个函数,该函数接受该字典并将其展开为以下形式的字符串:
AND (parameter1 = value1) AND ((parameter2 = value2.1) OR (parameter2 = value2.2)) AND ...
并通过此函数将该字符串插入到大型 SQL 语句中:
where 1 = 1
and selector in ('YES', 'NO')
{form_sql_statement_from_dictionary(dictionary)}
但似乎这种方法容易受到 SQL 注入攻击。现在,安全的方法是对大型 SQL 语句进行参数化,但由于我不知道字典中有多少参数和值,所以我不知道如何进行这样的参数化。此外,我无法更改大型 SQL 语句。不知何故,我必须以安全的方式形成该 AND/OR 语句并将其插入到现有字符串中。有什么办法可以做到这一点,而不是尝试管理字典值本身?
完整的 Python 脚本如下所示:
async def query_for_data(
connection: "PgService", dictionary: Dict[str, Any]
) -> pd.DataFrame:
sql = f"""
*multiple SQL statements*
where 1 = 1
and selector in ('YES', 'NO')
{form_sql_statement_from_dictionary(dictionary)}
"""
data = await connection.fetch(sql)
data = pd.DataFrame(res, columns=[k for k in res[0].keys()])
return data
函数如下所示:
def form_sql_statement_from_dictionary(
dictionary: Dict[str, Any]) -> str:
hashvalue = list(dictionary.values())
scope = hashvalue[0]["scope"]
dictionary_element_names = list(scope.keys())
statement_elements = []
for element_name in dictionary_element_names:
dictionary_element_values = scope[element_name]
if len(dictionary_element_values) == 1:
dictionary_element_value = dictionary_element_values[0]
statement_element = (
f"( {prefix}{element_name} = '{dictionary_element_value}' )"
)
statement_elements.append(statement_element)
else:
statement_or_elements = []
for dictionary_element_value in dictionary_element_values:
statement_element = (
f"{prefix}{element_name} = '{dictionary_element_value}'"
)
statement_or_elements.append(statement_element)
final_or_statement = "( " + " OR ".join(statement_or_elements) + ")"
statement_elements.append(final_or_statement)
final_statement = " AND " + " AND ".join(statement_elements)
return final_statement
解决方案
在此处找到详细信息:
首先,使用位置参数构建 SQL where 子句,例如...
(x.col1 = %s) AND (x.col2 = %s OR x.col3 = %s)
同时创建这些参数的列表
['foo', 'foo', 'bar']
然后使用参数化查询...
cursor = connection.cursor()
cursor.execute(sql, parameters)
data = await cursor.fetchall()
参数化将为您引用和转义所有参数,因此不会受到 SQL 注入攻击。
但是列名仍被直接替换到查询中。没有内置的方法可以保护您免受这种影响。如果用户可以直接控制这些字符串,他们仍然可以通过这种方式破解您。
因此,您必须通过适合您的用例的任何方式自行监管、验证这些列名称。
总而言之,修改后的 python 看起来像......
def form_sql_statement_from_dictionary(dictionary):
hashvalue = list(dictionary.values())
scope = hashvalue[0]["scope"]
dictionary_element_names = list(scope.keys())
prefix='ummmmmmmmmm.'
statement_elements = []
statement_params = []
for element_name in dictionary_element_names:
statement_elements.append(
' OR '.join(
f"{prefix}{element_name} = %s"
for item in scope[element_name]
)
)
statement_params += scope[element_name]
return '(' + ') AND ('.join(statement_elements) + ')', statement_params
async def query_for_data(
connection: "PgService", dictionary: Dict[str, Any]
) -> pd.DataFrame:
sql_where_clause, sql_params = form_sql_statement_from_dictionary(dictionary)
sql = f"""
*multiple SQL statements*
where 1 = 1
and selector in ('YES', 'NO')
and {sql_where_clause}
"""
cursor = connection.cursor()
cursor.execute(sql, sql_params)
res = await cursor.fetchall()
data = pd.DataFrame(res, columns=[k for k in res[0].keys()])
return data
推荐阅读
- java - Java 2D Game - 为适当的实体渲染攻击视觉效果
- java - 是否有任何关于内在方法的官方 Java 文档?
- opencart - 如果产品库存为 0,则隐藏添加到购物车按钮和选项
- python - 将字符串拆分为子字符串
- node.js - 将 Content-Type 设置为 json 但请求以 x-www-form-urlencoded 形式出现
- python - 无法使用 partitionByInstrument 将文件分成几部分
- node.js - 在 Windows 64 位系统上,'ng' 未被识别为内部或外部命令
- java - Eclipse 安装失败并出现错误
- reactjs - 如何减少生产中的 reactjs 文件大小?
- ios - 金属着色器在缓冲区中插入值