开源数据发布工具 Datasette 刚放出 1.0a38 版本,修复了一个不算致命但足够棘手的 SQL 注入漏洞:如果一个网站在同一个 SQLite 数据库里既放了公开表又放了私有表,靠 Datasette 自带的权限系统做隔离,那么任何能看到那张公开表的用户,理论上都能绕过限制,用注入手法读到本该私有的数据。
作者 Simon Willison 在公告里加了一句轻描淡写的自我评价:这种"同库混放公私表"的配置很少见,他自己都没见过。这句话听起来像是给读者吃了颗定心丸,但细看版本号,会发现公告本身就有一处容易让人误判的表述。
版本号里的坑:0.65.3不是安全版
公告写的是"这个修复同样体现在 Datasette 0.65.3 里",很多管理员看到这句话,第一反应是升级到 0.65.3 就没事了。
问题是,0.65.3 本身就是受影响版本。真正把漏洞堵上的是随后发布的 0.65.4,对应 1.0 分支上的 1.0a39。如果按公告字面意思停在 0.65.3,防线其实还是敞开的。
这不是文字游戏,是开源项目安全披露里常见的表达模糊——作者往往在同一句话里提到"漏洞"和"修复版本",但读者很容易把"讨论范围"当成"安全边界"。对照 Node.js、Django 这类项目的安全公告,通常会用"受影响版本 / 修复版本"两行分开写,Datasette 这次的表述显然不够清楚。
权限系统管住了表面,管不住SQL接口
Datasette 的权限模型分两层:view-table 控制谁能看某张表,execute-sql 控制谁能跑自定义 SQL。管理员一般的思路是——关掉 execute-sql,用户就只能规规矩矩地翻页浏览,碰不到私有表。
这次的漏洞出在表页面(table pages)本身。哪怕 execute-sql 权限被关闭,拥有任意一张表查看权限的用户,依然能在表页面构造注入,让数据库执行任意查询,间接读到同库里的私有表。换句话说,漏洞绕过的不是"没有权限的人",而是"部分权限的人"——这是一次典型的权限提升,不是从零到有的越权访问。
半个权限,在SQL注入面前和完整权限没什么区别。
好在这套攻击链有天然的上限:Datasette 对外暴露的 SQL 执行默认是只读的,不支持写入或代码执行。攻击者能拿到的最多是私有表的数据内容、表结构、跨表关联信息,谈不上篡改数据库或拿下服务器。这也是为什么官方定级没有拉到最高——影响集中在机密性泄露,不是完整性或可用性问题。
谁该现在就升级,谁可以先观望
真正暴露在风险里的,是那批在同一个数据库文件里混放了公开数据和敏感数据、又指望权限系统做隔离的管理员——常见于开放数据门户、数据新闻项目的内部工具、机构内部的数据共享站点。如果私有表里存了个人信息或者内部资料,风险不是"可能",是"配置本身就允许"。
- 风险.升级前若仍保留私有表的execute-sql权限,即便不升级也随时可能被现有用户注入读取。
对这类管理员,升级到 0.65.4 或 1.0a39 是最直接的解法,升级前的过渡期建议先把所有非信任角色的 execute-sql 权限收回,把敏感数据搬到完全独立的数据库文件里,并回头查一遍日志里有没有异常的 UNION、CTE 或 SQLite 元数据查询。如果之前的实例里存过密钥或凭证,轮换一遍更安心。
- 结论.把公开数据和私有数据分库存放,比指望权限系统兜底更靠得住。
对绝大多数只发布单一公开数据集、没有私有表混在同库里的用户,这次的补丁更像是一次例行维护,不需要连夜处理。
目前还看不清这个漏洞是否已经拿到 CVE 或 GHSA 编号,也没有公开信息显示它在真实环境里被利用过。对于像 Datasette 这样把"细粒度权限发布数据"当核心卖点的轻量级工具来说,这类边界问题大概率不是最后一次出现——值得留意的是官方后续会不会补一份更规范的安全公告,把受影响范围和修复版本说清楚,而不是留给用户自己去核对。
