Agent 上线前必查:文件系统访问的路径规范化审计清单
·

当 AI Agent 需要读写本地文件时,路径规范化(Path Normalization)是安全边界的第一道防线。未经处理的 ../../../ 式路径跳跃可能直接绕过沙箱,本文给出上线前必须核查的 7 类风险点与自动化检测方案。
为什么路径规范化≠简单字符串替换?
开发者在处理 data/../../etc/passwd 这类路径时,常犯三个错误: 1. 仅做字符串匹配:用正则删除 ../ 但未解析实际路径层级,导致 data/././../../ 仍可逃逸 2. 未绑定根目录:未用 realpath() 或 Path.resolve() 锁定基准目录,相对路径仍可回溯 3. 忽略符号链接:软链接可能指向沙箱外,需配合 O_NOFOLLOW 标志或 canonicalize_path
审计清单(Linux/macOS 示例)
1. 基准目录锁定
- [ ] 使用
chroot()或openat(dirfd)限定工作目录 - [ ] 检查所有文件 API 是否传递绝对路径(禁止拼接用户输入的相对路径)
- 技术细节:
chroot()需配合chdir()使用,且要求进程有 CAP_SYS_CHROOT 能力openat()的dirfd应来自预先打开的基准目录文件描述符- 对于容器化场景,需检查挂载点是否允许
..回溯到宿主机
2. 规范化函数选择
- [ ] Python:
pathlib.Path.resolve(strict=True)>os.path.abspath - [ ] Node.js:
fs.realpath.native+path.join替代手动拼接 - [ ] Rust:
std::fs::canonicalize但需处理ErrorKind::NotFound - 边界案例:
- Windows 下
Path.resolve()可能保留大小写(NTFS 不敏感但审计日志需一致) - Node.js 的
fs.realpath.native在 ARM 架构的 Alpine Linux 上可能有性能问题
3. 符号链接防御
- [ ] 关键操作添加
O_NOFOLLOW(open 系调用) - [ ] 或用
lstat()先检查文件类型(注意 TOCTOU 风险) - 深度防御:
- 结合
faccessat2()的RESOLVE_BENEATH标志(Linux 5.6+) - 对于临时文件,应使用
mkstemp()而非手动创建(避免竞态条件)
4. 沙箱逃逸测试用例
# 必须覆盖的测试路径样本
test_cases = [
"config/../../.ssh/id_rsa", # 向上跳转
"/tmp/symlink→/etc/passwd", # 符号链接
"a/././b/../../etc", # 冗余当前目录
"%2e%2e/%252e%252e%252f/etc" # URL 编码绕过
] - 扩展测试集: - Unicode 规范化攻击:\u202e 等双向控制字符 - 超长路径(超过 PATH_MAX 时各语言处理差异) - 设备文件路径(如 /proc/self/mem)
5. 错误处理
- [ ] 禁止将
realpath()的错误信息原样返回用户(泄露路径结构) - [ ] 统一转换为
PermissionDenied类泛化错误 - 日志策略:
- 生产环境应记录规范化前后的哈希值而非原始路径
- 对高频失败IP启用人机验证(防暴力探测)
6. 审计日志字段
- [ ] 记录原始输入路径与规范化后路径
- [ ] 对多次规范化失败的行为触发熔断
- 可观测性增强:
- 在 Prometheus 中监控
path_normalization_failures指标 - 通过 eBPF 跟踪
openat系统调用
7. 运行时加固
- [ ] 使用 Landlock 或 AppArmor 限制文件树访问范围
- [ ] 对 Python 等动态语言需审计
__import__('os').system调用 - 进阶方案:
- gVisor 的 Gofer 协议可拦截所有文件操作
- 对于敏感操作,通过内核模块实现路径白名单
工具推荐与集成
静态检测工具链
- Semgrep:
- 预置规则检测
os.path.join与用户输入拼接 - 自定义规则识别未处理的
../模式 - CodeQL:
- 构建数据流分析追踪路径传播
- 识别未经验证的
FileInputStream构造参数
动态测试方案
- Firejail 沙箱:
firejail --private-tmp --trace=open,openat \ -- ./your_agent --input=user_controlled_path - 通过日志分析实际访问的文件范围
- Ptrace 监控:
- 使用
strace -e file过滤文件系统调用 - 对容器内进程需附加
nsenter命令
CI/CD 集成示例
# GitHub Actions 示例
jobs:
path_audit:
steps:
- uses: returntocorp/semgrep-action@v1
with:
config: p/security-audit
- run: |
python -m pytest tests/test_path_normalization.py \
--junitxml=report.xml
firejail --trace=open \
-- ./agent --test-file-access
历史漏洞案例研究
- CVE-今年-4034(Polkit pkexec):
- 通过非规范化路径加载恶意库
- 修复方案:
execve()前强制PATH解析 - Python 打包目录穿越(今年):
tarfile.extract()未处理恶意压缩包路径- 现默认启用
filter='data'参数 - Node.js
fs.realpath缓存污染(今年): - 缓存未区分大小写导致绕过检查
- 现提供
fs.realpath.native规避
终极建议:在 Agent 设计早期即引入「最小文件权限」原则,所有文件访问应显式声明所需操作模式(如只读、追加),并通过 Landlock 等机制在内核层强制执行。对于金融、医疗等敏感场景,建议采用二次验证机制——当检测到路径包含
..或符号链接时,强制通过人工审批通道执行操作。
更多推荐




所有评论(0)