Skip to content

fix: 修复 upload_file 任意文件读取、downloadFile SSRF 等一批低风险 bug(v0.6.11) - #115

Open
crazywhalecc wants to merge 4 commits into
masterfrom
fix-low-risk-bugs
Open

fix: 修复 upload_file 任意文件读取、downloadFile SSRF 等一批低风险 bug(v0.6.11)#115
crazywhalecc wants to merge 4 commits into
masterfrom
fix-low-risk-bugs

Conversation

@crazywhalecc

Copy link
Copy Markdown
Member

概述

本 PR 修复一批不影响公共 API 的低风险 bug(安全、Workerman 驱动、Swoole 驱动、OneBot V12 构建),并配套新增回归测试。版本 bump 至 0.6.11。

修复内容

安全

  • upload_file 任意文件读取path 类型仅校验了 /..,绝对路径可直接读取服务器任意文件(如 /etc/passwd)。现限制为只能读取配置的上传目录内的文件(realpath 双端规范化前缀校验,../、符号链接逃逸、兄弟目录均不可绕过)
  • downloadFile SSRF:URL 校验只检查 scheme。现改为 fail-closed:
    • 拒绝全部内网/保留 IP(IPv4:0/8、10/8、100.64/10、127/8、169.254/16、172.16/12、192.168/16、组播与保留段;IPv6:::1::fc00::/7fe80::/10::ffff:0:0/96 映射地址)
    • 修复 AAAA 记录被跳过dns_get_record 对 AAAA 记录返回 ipv6 键而非 ip,原实现只读 ip 键导致仅解析到内网 IPv6 的域名完全绕过校验
    • DNS 解析失败改为拒绝(原 fail-open);拒绝十进制/八进制/十六进制/短横线等 IP 混淆形式;修复 ::ffff: 映射地址前 80 位未校验导致的合法全局地址误判
  • 分片上传状态无限增长upload_fragment 静态缓存无上限、无过期。新增 100 个 prepare 上限与 10 分钟过期清理,transfer 每次刷新过期时间(慢速大文件不会中途被拒);附带修复 md5(microtime) 快速连续 prepare 时碰撞导致分片互相覆盖的问题

Workerman 驱动

  • 对象池跨池串对象:等待协程队列由静态改为实例属性,多个池共存时不再互相 resume 串对象
  • AbstractObjectPool::return() 必然 TypeError:SplQueue 的 push 返回 void,在 strict_types 下触发 TypeError,按队列类型分支处理
  • 归还静默丢对象:协程环境失效时对象既不 resume 也不入队,改为回收到空闲队列
  • HTTP 异常连接挂起:请求处理异常发生在响应创建前时不发送任何响应,客户端永久等待,现兜底发送 500

Swoole 驱动

  • 删除 WebSocket 握手时的 echo 调试残留(每次连接向 stdout 输出 sec-websocket-key),保留 Sec-WebSocket-Key 校验逻辑
  • WSServerSocket::close() 未实现:永远返回 false 导致无法主动关闭连接,现基于 server->close($fd) 实现

其他

  • OneBotBuilder 键顺序敏感:组件构建严格比较键注册顺序,任意顺序链式调用即报错,改为无序比较
  • WebSocket 客户端 URI fragment 由 ? 修正为 #;Workerman 客户端端口为 null 时生成畸形 ws://host: URI,默认补 80
  • Driver 通信类型 switch 无 default,未知类型静默忽略,现输出警告日志

测试

  • 新增 6 个测试文件共 25 个用例:上传目录限制(含符号链接逃逸)、SSRF 校验(内网 IP/AAAA 记录/混淆形式/映射地址)、分片上限与过期刷新、OneBotBuilder 乱序构建、对象池跨池隔离、URI 构造
  • 全部用例已验证在修复前代码上真实失败(非假阳性)
  • composer test:100 tests / 351 assertions,剩余 1 error + 2 failures 为 PHP 8.5 环境存量问题(setAccessible 弃用、外部网络测试),与本 PR 无关
  • php-cs-fixer 通过

变更记录

4 个独立 commit:fix(安全)fix(Workerman)fix(驱动)bump version to 0.6.11

- upload_file 的 path 类型增加目录限制,仅允许读取配置的上传目录内文件(realpath 前缀校验,符号链接逃逸一并拦截)
- downloadFile 的 URL 校验改为 fail-closed:拒绝内网/保留 IP(含 IPv4/IPv6 各保留段)、DNS 解析失败拒绝、
  拒绝十进制/八进制/十六进制等 IP 混淆形式;修复 AAAA 记录(ipv6 键)被跳过导致仅 IPv6 域名绕过校验的问题
- 分片上传 prepare 增加 100 个上限与 10 分钟过期清理,每次 transfer 刷新过期时间;修复 md5(microtime) 快速连续 prepare 时碰撞覆盖的问题
- ObjectPool 的等待协程队列由静态改为实例属性,多个池共存时不再互相 resume 串对象
- AbstractObjectPool::return 对 SplQueue 的 push 返回 void 不再触发 TypeError
- 协程环境失效时归还的对象不再被静默丢弃
- HTTP 请求处理异常发生在响应创建前时,兜底发送 500 响应避免客户端永久等待
- 删除 Swoole WebSocket 握手时的 echo 调试残留,保留 Sec-WebSocket-Key 校验逻辑
- 实现 Swoole WSServerSocket::close,此前该接口永远返回 false 导致无法主动关闭连接
- WebSocket 客户端 URI fragment 由 ? 修正为 #,Workerman 客户端端口为 null 时默认 80
- OneBotBuilder 组件构建不再要求配置键的注册顺序
- Driver 未知通信类型不再静默忽略,输出警告日志
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant