自动发布并不等于让CI永远握着"直接上线"权限。npm新推出的阶段专用令牌,把流水线改成:机器准备版本,人确认后才公开。对维护者最直接的价值,是令牌即使泄露,也不能用npm publish塞进一个新版本;但它仍是写权限令牌,绝不能当成无害凭据。
发生了什么
GitHub在9月18日宣布,npm细粒度访问令牌新增Read and write (stage only)权限。工作流使用npm CLI 11.15.0或更高版本执行npm stage publish,把包版本送入待审状态;拥有发布权的维护者随后以双因素认证(2FA)批准。Node.js最低要求为22.14.0,现有包可直接使用。
一手来源是GitHub更新公告与npm阶段发布文档。公告还给出关键时间点:npm计划在2027年1月移除通过"绕过2FA"令牌直接发布的能力。阶段发布目前是自愿迁移,不会自动收紧旧令牌。
核心判断:把权限按风险拆开
传统自动发包把构建、上传和公开发布绑在同一个令牌上。一旦工作流依赖、第三方Action或日志泄露令牌,攻击者拿到的是面向所有用户的立即发布权。阶段专用令牌把"提交候选包"和"让候选包进入公共注册表"拆成两个主体完成,类似银行制单与复核分岗。
这个类比有边界:npm阶段令牌并非只读。官方明确说明,它仍可移动dist-tag、弃用版本等。这些动作足以把latest指向错误版本,或让正常版本显示为不可用。因此真正的安全增量,是切断"创建新公开版本"的路径,而不是消灭所有供应链风险。
一份可迁移的工作流
先在npm后台创建只覆盖目标包的stage-only令牌,保存为仓库机密NPM_STAGE_TOKEN。下面示例只展示阶段提交,不包含自动批准:
yaml
name: stage-package
on:
workflow_dispatch:
inputs:
version:
description: "待发布版本"
required: true
jobs:
stage:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: "22.14.0"
registry-url: "https://registry.npmjs.org"
- run: npm ci
- run: npm test
- run: npm version "${{ inputs.version }}" --no-git-tag-version
- run: npm pack --dry-run
- run: npm stage publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_STAGE_TOKEN }}
代码关键点有三个:workflow_dispatch避免每次合并都生成候选版本;npm pack --dry-run先检查真正会进入包的文件;令牌只通过环境变量注入,不能写入.npmrc并提交。依赖安装与运行条件是Node.js 22.14.0以上、npm CLI 11.15.0以上,以及包维护者权限和已启用2FA。
示例未在本次任务中实际运行,因为当前环境没有目标npm包、账户和stage-only令牌;YAML仅经过结构与字段人工检查,不能把它当成已成功发包的证明。
对开发团队有什么影响
小团队常把人工批准视为拖慢发布。更好的做法不是回到手工打包,而是让机器完成可重复工作,只把高风险的公开动作留给人。维护者审查时应看到版本差异、npm pack文件清单、测试结果、来源证明和变更日志,而不是只点一个"批准"。
对多包仓库,令牌应按包或包组分开,避免一个前端组件的CI可以操作整个组织的包。对无人值守的高频发布,如果平台支持,优先评估npm trusted publishing,用短期身份取代长期令牌;stage-only更适合暂时无法迁移、又需要在2027年政策变化前收紧权限的团队。
迁移顺序也很重要。不要第一天就替换所有生产令牌。先选一个低风险包创建候选版本,分别演练正常批准、审查拒绝、候选过期和同版本重试;确认告警能区分"提交失败"与"等待批准"。然后再把审计日志接入团队现有的安全事件系统,并设置发布窗口与备用批准者。这样做的目的不是增加会议,而是保证主要维护者离线时,团队不会为了赶版本临时恢复高权限令牌。
批准页面还应与源码提交建立稳定对应。候选包的版本号、Git提交摘要、工作流运行编号、依赖锁文件和包内容清单,至少要有一个不可混淆的关联键。如果审查者看到的只是1.2.3,攻击者仍可能利用版本命名和并发工作流制造误批。把构建产物哈希写入发布说明,可以让批准动作指向确定产物。
风险边界与行动清单
第一,阶段令牌仍能动dist-tag与弃用状态,必须像其他写令牌一样轮换、限制包范围并监控操作。第二,人类批准若只看版本号,双人流程会退化成橡皮图章。第三,2FA保护的是批准者账户,不能替代构建隔离、锁定依赖和来源证明。
本周就能做四件事:盘点所有NPM_TOKEN;标记哪些令牌可以直接发布;为关键包试建stage-only令牌;在演练包上验证提交、拒绝、批准和回滚。供应链安全最实用的改进,不是增加一条扫描规则,而是让被盗凭据少一项不可逆能力。
完成迁移后,记得主动吊销旧令牌,并检查缓存、日志和历史配置里是否还残留它。
你的发布流水线里,哪一步最应该从"机器直接执行"改成"人工明确批准"?
关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。
本文首发于 java4u.cn,转载请注明出处。