如何去除ClickHouse参数化查询中默认添加的单引号
你遇到的问题根源在于substitute_params方法的设计定位:它是专门为SQL值参数提供的注入防护机制,会自动为参数添加单引号,但库名、表名这类属于SQL标识符,不能用这种方式处理。
要在避免单引号的同时防范SQL注入,推荐以下方法:
使用quote_identifier安全转义标识符
clickhouse_driver提供了quote_identifier方法,专门用于安全转义SQL标识符(库、表、列名等),它会用ClickHouse支持的反引号包裹标识符,并正确转义内部的特殊字符,既保证SQL语法正确,又能防范注入风险。
示例代码:
from clickhouse_driver import Client c = Client(host="localhost") db_name = "test" table_name = "t" # 安全转义标识符 quoted_db = c.quote_identifier(db_name) quoted_table = c.quote_identifier(table_name) # 拼接查询语句 query = f"SELECT * FROM {quoted_db}.{quoted_table}" print(query)
执行后输出的SQL为:
SELECT * FROM `test`.`t`
ClickHouse完全支持反引号包裹的标识符,不会出现语法错误,同时即使传入带有特殊字符的标识符(比如包含空格、单引号或恶意注入内容),quote_identifier也会正确转义,避免执行恶意SQL。
为什么不能用substitute_params处理标识符
substitute_params的核心逻辑是把参数当作SQL值处理,所以会自动添加单引号,这就导致原本的标识符被转换成了字符串常量,触发SQL语法错误。比如你示例中生成的SELECT * from 'test'.'t',ClickHouse会把'test'和't'当作字符串,而不是库表名,自然无法正确执行。
绝对不要直接拼接未转义的标识符
如果直接用f-string拼接原始的库表名(比如f"SELECT * FROM {db_name}.{table_name}"),当传入的标识符包含恶意内容时(比如db_name = "test'; DROP TABLE users; --"),就会直接执行恶意SQL,造成数据泄露或破坏,必须用quote_identifier做安全转义。
内容的提问来源于stack exchange,提问作者Odess4

