clickhouse不支持真正的preparedstatement,安全执行需依赖原生协议参数化、白名单校验、手动转义及权限隔离。使用clickhouse_driver时须用cursor.execute(query, params)传tuple/list;http接口必须对用户输入调用escape_string;动态字段名、操作符等须严格白名单限制;数组/元组参数应由驱动原生支持而非字符串拼接;同时需配置settings和用户权限防全表扫描与危险操作。

ClickHouse 不支持传统 PreparedStatement,但仍有安全执行路径
ClickHouse 的原生 HTTP 接口(port=8123)和 TCP 接口(port=9000)均不支持 JDBC 风格的 PreparedStatement 协议语义。所谓“预编译占位符”在 ClickHouse 中只是字符串替换,数据库端不会做参数绑定或类型隔离。这意味着直接拼接用户输入到 SQL 字符串中(哪怕用了 ? 或 %s)仍可能触发注入。
真正起作用的是:ClickHouse 官方驱动(如 clickhouse_driver)在底层将参数序列化为二进制格式并通过 Native 协议传输,由服务端按列类型严格解析——这才能规避注入。HTTP 接口则完全依赖客户端自行转义。
- 用
clickhouse_driver时,必须使用cursor.execute(query, params)形式,且params是 tuple/list,不能是 f-string 或format() - 用 JDBC(如
clickhouse-jdbc)时,PreparedStatement仅在驱动层做字符串插值,**不安全**;应改用Statement+ 手动转义,或切换至 Native 协议连接 - HTTP 接口(
curl/requests)必须对所有用户输入调用escape_string(Python)或clickhouse_escape(JS 库),不可信任json.dumps或 URL 编码
动态 WHERE 条件拼接时,白名单校验比转义更可靠
分析类查询常需根据前端筛选条件动态生成 WHERE 子句,比如 WHERE status IN ? AND region = ?。若字段名(如 status)、操作符(如 IN、LIKE)、表名都来自用户输入,仅靠参数化无法覆盖。
此时必须引入白名单机制,把可变部分限制在可信范围内:
- 字段名只允许从预定义集合中选取:
['status', 'region', 'category', 'created_date'] - 操作符限定为:
['=', '!=', 'IN', 'NOT IN', 'LIKE', 'BETWEEN'],且对LIKE的通配符做二次过滤(禁止开头用%) - 枚举值类参数(如状态码)走字典映射:
{'active': 1, 'inactive': 0},而非直接透传字符串 - 避免让用户决定是否加
OR/AND,用结构化条件对象替代自由文本
数组和嵌套结构参数需额外处理,否则绕过参数化
ClickHouse 的数组类型(Array(String))和元组(Tuple)在参数化接口中容易被误用。例如 MyBatis 中写 array(${ids}) 属于字符串拼接,即使 ${ids} 是 List,也会被直接展开为未转义的 'a','b','c',导致注入。
安全做法是依赖驱动原生支持:
-
clickhouse_driver支持直接传 Python list 给IN子句:cursor.execute("SELECT * FROM t WHERE id IN %s", ([1,2,3],)) - Java 中若用
clickhouse-jdbc,需确认版本 ≥ 0.4.6,且启用use_server_time_zone=true等安全选项,再配合setArray()方法 - 禁用任何形如
array(#{list})或tuple(#{a}, #{b})的 MyBatis 表达式,它们本质仍是字符串插值
权限隔离和查询沙箱是最后一道防线
即使代码层做了参数化和白名单,失控的查询仍可能拖垮集群。ClickHouse 的 SETTINGS 和用户权限必须同步收紧:
- 为应用用户设置
max_rows_to_read = 10000000、max_bytes_to_read = 1073741824,防全表扫描 - 禁用危险函数:
SETTINGS allow_experimental_map_type = 0、allow_ddl = 0 - 用
readonly = 2限制用户只能执行SELECT,且不能访问系统表(system.*) - 敏感表(如含用户标识的日志)单独建视图,用
MATERIALIZED VIEW脱敏后再授权
最易被忽略的是:HTTP 接口默认允许执行任意 SQL,包括 KILL QUERY 或 SYSTEM DROP DNS CACHE。生产环境务必关闭 allow_introspection_functions 并启用 user_directives 白名单。











