shy3130
a4e580e314
fix(security): 修复 ext_columns DuckDB SQL 注入 (closes #150)
Issue #150 报告了 ext_columns 功能的 DuckDB SQL 注入, 经核查属实:
field_name 经裸双引号拼进 SQL, 攻击者可借 COPY TO 写文件 (RCE) 或
UNION 读数据。已修复, 详见 PR。
根因: 3 个 SQL sink 用未转义的 f"{field_name}", 仅 screener:147 一处
用 _quote_ident 转义; 且 parser 校验不一致 (screener 校验 config_id,
kline/watchlist 零校验)。
修复方案:
1. 新增 backend/app/db_safe.py 集中定义 quote_ident (双引号转义, 对任意
字符安全) + is_valid_ext_ident (config_id 白名单), 消除不一致根因
2. 3 个 sink 统一改用 quote_ident:
- screener.py:785 (limit_ladder)
- kline.py:376 (_attach_ext, GET /api/kline/daily)
- watchlist.py:279 (watchlist_enriched)
3. kline + watchlist parser 加 config_id 白名单 (screener 已有)
4. screener 复用共享原语, 删除私有 _quote_ident/_EXT_IDENT_RE
关键约束:
- field_name 不能加白名单: FieldDef.name 无校验 + infer_fields_from_df
直接采用原始 CSV/Parquet 列名, 合法可含中文/点。故只在 sink 转义。
- 不关闭 enable_external_access: 实测会同时禁用 read_parquet (项目核心
数据访问方式), 不可行。
对 Issue #150 报告的澄清:
- sink #2/#3/#4 (field_name 注入) 属实, 已修
- sink #1 (screener.py:343 conditions[]/order_by) 不存在, 为误报
(screener.py 无 conditions/order_by/execute 调用)
验证: 后端全量 527 passed (含新增 24 个安全测试, 含 2 个端到端注入
测试: COPY TO 不产生文件 / UNION 不泄漏数据 + 合法特殊字段名回归)
2026-07-31 19:56:33 +08:00
..
2026-07-26 11:56:10 +08:00
2026-07-18 18:31:02 +08:00
2026-07-14 11:51:35 +08:00
2026-07-18 16:59:53 +08:00
2026-07-12 13:52:49 +08:00
2026-07-25 22:51:27 +08:00
2026-07-26 18:35:31 +08:00
2026-07-08 13:42:53 +08:00
2026-07-15 21:34:44 +08:00
2026-07-18 00:49:28 +08:00
2026-07-16 14:34:07 +08:00
2026-07-31 19:56:33 +08:00
2026-07-19 16:42:03 +08:00
2026-07-16 12:17:27 +08:00
2026-07-16 12:17:27 +08:00
2026-07-31 19:38:36 +08:00
2026-07-26 19:59:10 +08:00
2026-07-18 00:49:28 +08:00
2026-07-28 09:46:51 +08:00
2026-07-26 19:59:10 +08:00
2026-07-08 12:12:29 +08:00
2026-07-28 21:13:10 +08:00
2026-07-09 17:38:44 +08:00
2026-07-18 16:59:53 +08:00
2026-07-20 10:33:06 +08:00
2026-07-19 09:36:55 +08:00
2026-07-19 10:06:26 +08:00
2026-07-28 21:13:10 +08:00
2026-07-19 10:06:26 +08:00
2026-07-30 13:09:07 +08:00
2026-07-09 19:14:50 +08:00
2026-07-26 18:35:31 +08:00
2026-07-26 18:35:31 +08:00
2026-07-20 13:01:03 +08:00
2026-07-20 13:01:03 +08:00
2026-07-16 12:17:27 +08:00
2026-07-18 23:37:54 +08:00
2026-07-26 18:35:31 +08:00
2026-07-08 14:19:52 +08:00
2026-07-16 14:34:07 +08:00
2026-07-20 13:01:03 +08:00
2026-07-18 16:59:53 +08:00
2026-07-22 11:21:13 +08:00
2026-07-08 11:27:59 +08:00
2026-07-26 16:08:42 +08:00
2026-07-08 12:34:08 +08:00
2026-07-16 12:17:27 +08:00
2026-07-16 12:17:27 +08:00
2026-07-16 12:17:27 +08:00
2026-07-18 16:59:53 +08:00
2026-07-28 21:13:10 +08:00
2026-07-26 18:35:31 +08:00
2026-07-26 12:19:55 +08:00
2026-07-26 18:35:31 +08:00