在开源记账工具 Akaunting 中,一名只读会计可以取消发票并擦除付款历史,使小企业面临隐蔽的数据丢失风险。该漏洞已在 3.2.0 版本中修复,但这种将权限检查与硬编码的方法名列表绑定的错误,仍然威胁着任何依赖基于角色的访问控制(RBAC)的系统。

漏洞是如何产生的

Akaunting 的 API 通过查询方法名白名单来验证用户的权限。该列表涵盖了常规的 CRUD 操作——创建、读取、更新、删除——但遗漏了几个更改状态的端点:

  • markSent
  • markCancelled
  • markReceived

由于这些处理程序不在列表中,框架在运行时从未调用权限检查程序。拥有只读角色的用户只需向 markCancelled 端点发送一个简单的 GET 请求,系统就会将其视为合法的状态变更。

取消发票不仅仅是将文档标记为无效,它还会删除与该发票关联的所有付款记录。结果是:没有编辑权限的用户可以抹除交易的财务轨迹。

测试结果显示

该漏洞出现在 Akaunting 的官方 Docker 镜像中:

  • 向更新发票发送标准的 PUT 请求返回了 403 Forbidden,证实了常规更新路径是受保护的。
  • 向取消端点发送 GET 请求却成功执行且未出现授权错误,暴露了这一漏洞。

为什么这很重要

财务报表可以在没有清晰审计追踪的情况下被篡改,这使得欺诈行为更难被发现,诚实的错误也更难被纠正。

修复方案

3.2.0 版本扩展了权限映射,将之前遗漏的状态操作包含在内。从该版本开始,任何更改文档状态的请求——无论是标记为已发送、已取消还是已接收——都必须通过与标准更新相同的角色验证。这恢复了“只读角色确实无法修改数据”的预期。

给开发者的教训

  • 永远不要将方法名等同于安全性。 添加新端点并不会自动继承保护;应审计每个公共方法是否存在副作用。
  • 白名单的完整性取决于列表本身。 一个静态的“合法”谓词列表会为疏忽留下隐患。
  • 将意图与 HTTP 谓词分离。 GET 旨在用于只读操作,但在本例中它执行了状态变更。应将变更操作限制在 POST、PUT、DELETE、PATCH 中。
  • 自动化权限覆盖检查。 静态分析工具可以标记出缺少授权调用的控制器方法,在发布前发现漏洞。
  • 使用最小权限账户进行测试。 基于 Docker 的测试使用了只读用户;在 CI 流水线中模拟此类场景可以及早发现类似问题。

后续关注

Akaunting 社区已经发布了修复版本。管理员应核实其实例的版本并及时进行更新。

对于构建任何基于角色的系统的开发者来说,教训是显而易见的:一个依赖于记住每种可能操作的权限模型在设计上就是脆弱的。应明确声明哪些操作会修改状态,在框架层面强制执行检查,并定期审计代码库。只有这样,“只读”标签才能真正被信任,从而确保财务记录的完整性。