One self-hosted console to run your entire business — commerce, ERP, HRM, CRM & manufacturing

谁能做什么?成长型团队的角色权限与审计日志实践

一个共用的管理员账号,加上一次被遗忘的周末升职,就可能让零售商损失数千美元。本文讲如何设计角色、禁止高风险职责组合、按金额给退款设关卡,并保留一份没人能悄悄改动的审计日志。

Author

Anichur Rahaman

5 天前11 min read2 views
谁能做什么?成长型团队的角色权限与审计日志实践

设想一家三家门店的零售商,店主在周一早上打开退款报表,发现周日 2 号店发出了 41 笔退款,合计 2,870 美元。其中 38 笔在 50 到 300 美元之间,按规定本该由第二个人批准。可这些退款全部由同一个收银员账号发起,批准的也是同一个账号。

没有人入侵系统。两年前的一个忙碌日子,这名收银员被临时授予了班组长角色,说是“只管这个周末”,之后再也没有收回。系统只是照指令办事。这是一个用于说明的假设场景,但这种模式很常见:权限发得匆忙,从不复核,留下的日志也没人看。

本文讲三件能避免这类事故的事:按岗位需要设计角色,凡是动钱或动库存的操作都要两个人,以及不能被悄悄改动的审计日志。文中附有三家门店零售商的角色模板,以及一笔退款的审计记录字段。

团队变大后,权限是怎么失控的

三个人的团队里大家彼此熟悉,共用一个管理员账号似乎无伤大雅。到了三家门店十五个人,同样的习惯就成了风险。原因都很平常:

  • 权限蔓延。员工换了岗位,旧权限还在,因为加权限只要一分钟,收权限却没人负责。
  • 复制账号。新人被“照着小林的样子”开通,连小林 2024 年为某个项目临时拿到的权限也一并带上。
  • 共用账号。整个班次共用一个收银台账号,日志里记的是“till”,而不是某个人的名字。
  • 遗留权限。离职员工的账号,或者他创建的 API 密钥,几个月后依然有效。

这里的多数损失并非惊心动魄的攻击,而是某个有权限的人一次次做下的小动作,没人注意。在所有损失来源中,内部这一类恰恰是零售商最能掌控的。

角色、权限和范围:三个不同的概念

基于角色的访问控制(RBAC)听上去抽象,拆开来看就清楚了。权限是对某类对象的一个动作:refund.issue、refund.approve、stock.adjust、customer.export。角色是与某个岗位对应的一组权限,有个名字:收银员、班组长、会计。范围则说明角色在哪里生效:一家门店、一个仓库,还是全部。

真正重要的单位是授权记录,而不是角色。“小苏是 2 号店的班组长”就是一行记录:用户、角色、范围、开始日期,以及可选的结束日期。小苏调到 3 号店,这一行就跟着变,她不会悄悄保留 2 号店的权限。

最小权限的实际做法

最小权限(least privilege)指一个人只拥有完成今天工作所需的最小权限集合。NIST SP 800-53 的 AC-6 控制项是这样表述的:系统只授予完成指定任务所必需的最严格的权限。放到零售系统里,就是三个习惯。

  • 角色从岗位职责出发设计,而不是从“万一要用到什么”出发。
  • 多设细粒度的小权限,少设笼统的大权限,让“查看销售”和“导出全部客户”成为两个独立开关。
  • 范围默认限定在一个地点。全部门店的权限是例外,旁边必须写着负责人的名字。

职责分离:不能集中到一个人手里的组合

最小权限限制的是一个人能做什么,职责分离限制的是一个人单独能做什么。NIST AC-5 的描述是:把职能分给不同的人,使滥用权限必须靠串通才能完成。做法很简单:列出那些由一人同时掌握就可能造成或掩盖损失的操作组合,然后让系统拒绝这种组合。

八项职责的冲突矩阵。红色格子是同一个人不能同时拥有的组合:创建与支付供应商、发起与批准退款、修改与运行工资、调整库存与批准盘点。一组橙色组合,调整库存与发起退款,需要复核。
四组组合直接禁止。另有一组只在有复核时才允许,因为库存调整可能掩盖退款舞弊。

这四组被禁止的组合,覆盖了一家商业企业里大部分的资金。既能创建供应商又能给供应商付款的人,可以凭空捏造一个供应商。既能发起退款又能批准退款的人,比如周一早上那位收银员,等于自己批准自己。修改工资和运行工资是同一个道理。调整库存的人又去批准用来佐证调整的盘点,亏损就是这样变得看不见的。

这条规则必须由软件在操作发生的那一刻强制执行,只写在制度文件里不算数。“批准人必须与申请人是不同的用户”这个校验只需一行逻辑,就能消除整整一类问题。

敏感操作需要一道关卡,而不只是登录

登录证明你是谁,并不证明这一次具体操作是否合理。对少数几类操作,如退款、改价、超过额度的折扣、库存调整、客户数据导出和发薪,要加一道按金额判断的关卡。

下面的流程使用的是示例阈值。你自己的阈值,应该按照在你的业务里一次出错的代价来定。

一笔退款的流程图。先按门店检查权限,结果是拒绝,或进入 50 美元的阈值判断。小额退款立即执行;大额退款交给不是申请人的批准人,然后执行或被驳回。每条路径最终都写入一条审计记录。
每个分支都通向同一个终点:一条记录。被拒绝和被驳回的尝试同样会写下来。

这个流程里有三个细节,比看上去重要得多。

  1. 权限检查包含范围。仅仅“拥有 refund.issue”不够,要问的是“在 2 号店是否拥有 refund.issue”。
  2. 批准人要与申请人比对,而不只是看权限。班组长不能批准自己发起的退款。
  3. 拒绝也要记录。一个收银员一周内四次尝试退 400 美元都被拒绝,这本身就是信息。只记成功,这条信息就丢了。

对最高一档,这里是 300 美元以上,要求两位批准人,例如门店经理加一位财务人员。档位要少。三档在收银台前讲得清楚,七档只会被人绕过去。

三家门店零售商的角色模板

这张表是起点,不是标准。“本店”表示权限只在用户所属门店生效。“申请”表示此人可以发起操作,但必须由别人批准。

权限收银员班组长门店经理库管员会计老板
收银台销售本店本店本店否否全部
发起 50 美元以内的退款本店本店本店否否全部
批准 50 至 300 美元的退款否本店本店否否全部
批准 300 美元以上的退款否否本店(两人中的第一位)否第二批准人全部
改价否申请本店,限定幅度内否否全部
调整库存否否否本店否全部
批准库存盘点否否本店否是全部
创建供应商否否否否否全部
向供应商付款否否否否是否
导出客户名单否否否否否全部
管理用户和角色否否否否否全部,需另一位老板批准

请特别留意这些空缺。会计可以给供应商付款,却不能创建供应商。老板可以创建供应商,却被禁止付款,这有点别扭,也是有意为之。老板这一列只表示角色能做什么,冲突检查仍然在每一笔交易上运行,所以老板也不能批准自己发起的退款。工资相关的角色没有放进这张表,它们通常属于单独的人力资源模板,规则相同:改工资的人不运行发薪。

审计记录必须包含什么

审计日志事后要回答一个问题:谁,在哪条记录上,什么时候,为什么,经谁许可,做了什么。一行“退款已处理”回答不了。下面是上述流程中那笔 184 美元退款的记录。

字段示例值为什么需要
记录编号和时间R-20931,09:41:07 UTC用于排序和查找。以 UTC 存储,以本地时间显示。
操作人cashier-07(实名用户,绝不是“till”)可追责
操作和对象对订单 48211 执行 refund.issue哪条记录发生了变化
地点2 号店,3 号收银机,设备和 IP范围核对和异常位置检测
金额和原因$184.00,原因代码“damaged”按原因分析规律
前后状态款项已收取后退回;库存 +1,进入退货箱证明产生的效果
适用规则“50 至 300 美元的退款:一位批准人”显示当时生效的政策
批准人和时间lead-02,09:42:31 UTC证明有第二个人
结果已完成成功、拒绝或驳回
上一条哈希和本条哈希7f3a…c91e防篡改

防篡改、保留期限和复核

管理员能随意修改的日志,证明力很弱。有两个低成本的办法。第一,让这张表对应用只能追加(append-only):没有更新或删除权限,更正以新增一行的方式写入。第二,把记录串成链。每一行保存自身内容加上上一行哈希的 SHA-256 值,改动任何一条旧记录,后面所有哈希都会对不上。每晚把日志复制到应用无法写入的存储中。

保留期限取决于你适用的规定。作为一个参照,PCI DSS v4.0.1 的要求 10.5.1 规定审计日志历史至少保留 12 个月,其中最近三个月要能立即用于分析。此外还要核对税务、薪酬和隐私方面的规定,并把保留期限写下来。

最后一个问题是谁来看。没人复核的日志只是一本日记。指定一位门店之外的负责人,比如财务主管或老板,给他三个保存好的视图:按用户统计的退款、手工库存调整、导出操作。每周十分钟就够,足以赶在周一之前抓住周一的那位收银员。

入职、调岗和离职

多数权限问题出在岗位变动的时刻,而不是遭到攻击的时候。把这三个时刻当作一套顺序固定的流程来做。

三栏:入职、调岗、离职。入职者获得限定在一家门店的模板角色。调岗者先失去旧角色,临时代班权限到期失效。离职者在离职谈话前被停用账号,密钥被轮换。三栏之下是季度复核。
权限要跟着岗位走:按模板授予,调岗当天调整,离职谈话之前收回。

入职:经理按模板申请角色,角色负责人批准,本人在首次使用前绑定第二重登录验证。调岗:先移除旧角色,再添加新角色。这个顺序可以防止权限蔓延。离职:停用账号(不要删除,历史必须保留),结束会话,轮换共用密码和 API 密钥,并把未完成的审批转交给他人。

临时权限需要单独的规则。“周五之前替小林代班”应当是一条带结束日期的授权记录,到期自动失效,不必有人记得。只要有这一项功能,周一场景里那次周末升职就不会留下来。

季度权限复核,逐步来做

每一条角色授权,至少要定期由人确认一次。PCI DSS 7.2.4 对范围内的系统规定最长六个月一次;对人员流动较大的零售商,每季度一次更合适。准备充分的话,复核一小时就能完成。

  1. 导出所有授权记录:用户、角色、范围、开始日期、结束日期、最近登录时间。
  2. 把各门店的名单发给对应的门店经理,请他们逐行回答是或否。
  3. 移除 60 天没有登录的账号,以及已经不在职的人员。
  4. 标出同时拥有上述矩阵中任一禁止组合两半的用户。
  5. 列出所有“全部门店”范围的授权,请老板逐一点名重新批准。
  6. 记录谁做了复核、何时做的、改了什么。复核本身也是一条审计记录。

该衡量什么

几个数字就能看出控制措施是否有效,哪一个都不需要专门做一个仪表盘项目。

  • 自批自办的敏感操作:目标为零。只要大于零,就说明规则可以被绕过。
  • 停用离职人员账号的时间:目标是当天,最好在离职谈话之前。
  • 60 天内无登录的账号:每个季度向零靠拢。
  • 按用户统计的退款率,与同一门店的同事对比。离群值是一个疑问,不是定论。
  • 每周被拒绝的尝试次数:少量属于正常,某个人突然集中出现一批就值得谈一谈。

回到周一早上

把开头的场景在这些控制措施下重放一遍。收银员的角色只包含销售和 50 美元以内的退款。184 美元的退款会交给另一位班组长,系统不会接受同一个账号出现两次。周末那次升职带有结束日期,周一就自动失效。

那 41 笔退款不会再按原样发生。即便还有几笔可疑,店主每周十分钟的复核就能发现,因为每一笔都带着用户、规则、批准人和哈希。她现在读的是报表,而不是事后才发现亏损。

要点回顾

  • 把权限、角色和范围分开,用带起止日期的授权记录来管理访问。
  • 落实最小权限,每个角色的范围默认限定在一个地点。
  • 在软件里禁止四组经典冲突组合:创建与支付供应商、发起与批准退款、修改与运行工资、调整与批准库存。
  • 按金额给敏感操作设关卡,校验批准人不是申请人,并且既记录成功也记录拒绝。
  • 让审计日志只能追加、哈希成链,按你适用的规定保留,并指定一个人每周复核。
  • 入职、调岗、离职按固定顺序办理,每季度复核一次所有权限。

Anichur Rahaman 是一名软件架构师,也是 StoreConsole 的创建者。他为成长型企业设计电商与 ERP 系统,专注于事件驱动架构、数据完整性和自托管部署。

About the Author

Anichur Rahaman

Continue Reading