返回一个文件,为何越过整个目录?Khoj 静态资源路由的安全边界

返回一个文件,为何越过整个目录?Khoj 静态资源路由的安全边界

一、背景与时间线

已确认事实: 项目公告于 2026-06-24 发布,GitHub 已审核数据库于 2026-09-25收录。此次时效点是新收录,不是新发生的攻击。核验页面未列出已知 CVE,本文使用 GHSA 标识。

项目 已确认范围
Python 包 khoj
受影响版本 >=2.0.0-beta.23,<2.0.0-beta.25
最低修复版本 2.0.0-beta.25
涉及功能 首页静态文件路由
核心影响 未认证文件读取,受服务进程权限约束

版本范围以数据库和项目公告为依据。不能把所有 Khoj 历史版本都写成受影响,也不能因为部署了登录页面就推断该资源路由受保护。

二、技术原理:路径对象不是授权对象

公告指出,/home/{file_path:path} 将调用者给出的路径与首页目录连接,交给 FileResponse。缺失的是最终目标与允许目录之间的包含关系检查。FileResponse 的职责是返回指定文件,不会自动知道业务想保护哪个目录。修复提交针对首页文件服务范围进行收紧。

**工程分析:**路径至少有三种不同表示:请求中的文本、程序拼接后的路径、文件系统解析后的对象。安全判断如果只发生在第一种表示上,后续规范化或链接解析可能改变实际目标。一个文件名也许没有明显的危险后缀,但这并不能证明它处于允许的目录。

另一个常见错误是字符串前缀判断:目录 /srv/site 与 /srv/site-backup 有共同前缀,却不是包含关系。目录边界必须按路径组件验证。对允许写入的目录,还要考虑检查与打开之间的竞争条件;一次 resolve() 并不提供原子文件访问保证。

三、影响范围:读取能力要与部署权限相交

**工程推断:**真实暴露面由"路由可达性、漏洞版本、服务账户可读范围"共同决定。容器挂载的配置、共享卷和开发工作区会扩大可读集合;只读挂载可以限制修改,却不能防止读取。

文件读取也不自动等于远程代码执行。是否可能进一步影响数据库或云服务,要看可读内容中是否存在有效凭据及这些凭据的权限。排查应记录证据,而不是把所有潜在链路都写成已发生事件。

四、防御性实验:只读取临时测试文件

下面是原创概念模型,不是 Khoj 官方补丁。它创建两个无敏感内容的目录,验证允许目录内正常读取,以及越界目标被拒绝。Python 3.9 及以上可运行,不联网、不读取系统文件。

python 复制代码
from pathlib import Path
from tempfile import TemporaryDirectory

def confined_read(root, relative):
    base = root.resolve()
    user = Path(relative)
    if user.is_absolute():
        raise ValueError("absolute path rejected")
    target = (base / user).resolve()
    if not target.is_relative_to(base):
        raise ValueError("outside allowed root")
    return target.read_text(encoding="utf-8")

with TemporaryDirectory() as d:
    lab = Path(d)
    public = lab / "public"
    public.mkdir()
    (public / "hello.txt").write_text("public sample", encoding="utf-8")
    (lab / "note.txt").write_text("lab sample only", encoding="utf-8")
    assert confined_read(public, "hello.txt") == "public sample"
    try:
        confined_read(public, "../note.txt")
    except ValueError:
        pass
    else:
        raise AssertionError("boundary failed")
print("directory boundary checks passed")

该实验验证包含关系,不覆盖并发符号链接替换、Windows 特殊路径或代理的 URL 解码差异。生产实现优先采用经过维护的静态文件组件,并审查符号链接策略;有敌对本地写入者时,需进一步采用与平台相适配的安全打开机制。

五、研发与安全团队行动清单

**立即处理:**从运行环境读取实际安装版本,升级到包含修复的版本;核对镜像、启动路径和副本,避免只改依赖声明。不能立刻升级时,限制相关入口可达性,评估是否可暂时移除自定义静态路由。

**代码治理:**集中处理静态资源,不让多个路由重复实现路径安全。资源标识与磁盘路径分离,优先通过服务端映射表选择文件。测试不仅要检查响应码,还要断言被拒绝请求从未打开越界文件。

**事件排查:**保留反向代理与应用日志,对异常路径、异常响应体积和非资源扩展名进行关联分析。日志不存在命中并不能证明没有读取;日志采样、路径归一化和保留期限都会影响证据完整性。只有确认或合理怀疑凭据暴露时,才根据权限和依赖关系开展轮换。

**持续验证:**在测试环境组合正常文件、目录同名前缀、父目录引用、链接和不存在文件等用例。对每个用例同时记录授权结果、实际访问目标和日志事件,防止"返回错误但已经产生副作用"。

六、总结

静态文件服务的安全属性不是"用了路径库",而是最终打开的对象始终属于被允许的资源集合。版本升级关闭已知入口,组件化路径处理、最小文件权限和有副作用断言的测试,才能让这一性质长期成立。

相关推荐
程序员-Benothing1 小时前
Linux内核与模块管理:uname、lsmod、modprobe、sysct
linux·运维·数据库
cmes_love2 小时前
期货期权L2五档行情、逐笔成交、分钟日线数据字段速览
数据库
神仙别闹2 小时前
基于C语言实现 TINY+ 编译器
c语言·开发语言·数据库
云杂项2 小时前
A Survey of Privacy Attacks in Machine Learning(个人笔记)
人工智能·安全·机器学习
adinnet20262 小时前
意图识别(Intent Recognition):智能体怎么听懂你想干啥
大数据·数据库
其实防守也摸鱼2 小时前
内网安全防护实践:从攻击视角理解防御要点
java·前端·安全·架构·自动化·nps
笨小孩@GF 知行合一3 小时前
易语言-基础-外部数据库
数据库·编程·易语言·中文编程
DolphinDB智臾科技3 小时前
告别繁琐解析!BinaryTcp 模块实现沪深 Level‑1 行情接收‑解析‑落库一站式能力
数据库·插件·dolphindb
weixin_404551243 小时前
Oracle SQL 优化知识库:构建方案与价值评估
数据库·sql·oracle