依赖安全扫描与供应链安全
1. 威胁模型
前端项目 90% 代码是依赖。供应链攻击:
- typosquatting:
reacttlodahs等假包 - 依赖投毒:合法包被劫持发恶意版本(event-stream、ua-parser-js 历史事件)
- 传递依赖:你装 1 个包,背后 200 个间接依赖
- CVE 漏洞:lodash <4.17.21 prototype pollution
- 生命周期脚本:
postinstall任意执行代码
2. npm audit
npm audit
npm audit --audit-level=high
npm audit fix # 自动升级修复
npm audit fix --force # 含 breaking change(慎用)
audit-level:info | low | moderate | high | critical,只报指定及以上。
CI 集成:
- run: npm audit --audit-level=high
发现高危依赖 CI 失败。
3. 锁文件(lockfile)
package-lock.json / yarn.lock / pnpm-lock.yaml 锁定所有依赖(含传递)的精确版本 + integrity hash。
永远提交 lockfile 到 git。
npm ci # 严格按 lockfile 安装(CI 用)
npm install # 可能改 lockfile(开发用)
npm ci vs npm install:
- ci:删除 node_modules 全装,不改 lockfile
- install:增量装,可能改 lockfile
4. 第三方扫描工具
4.1 Snyk(最强)
npm i -g snyk
snyk auth
snyk test # 当前项目
snyk monitor # 持续监控
snyk container test myapp # 镜像扫描
GitHub App 集成自动 PR 修复。
4.2 Dependabot(GitHub 内置)
.github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
groups:
dev-deps:
dependency-type: "development"
自动开 PR 升级有漏洞 / 过时的依赖。
4.3 Renovate
更强大的依赖更新工具,可配置:
- 自动 merge minor / patch
- 限制时间窗
- 分组更新
// renovate.json
{
"extends": ["config:base"],
"schedule": ["before 9am on Monday"],
"automerge": true,
"automergeType": "branch",
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
},
{
"matchPackagePatterns": ["^@types/"],
"groupName": "type definitions"
}
]
}
4.4 OSV-Scanner(Google)
brew install osv-scanner
osv-scanner -L package-lock.json
5. 包来源验证
5.1 npm registry 配置
# 用官方 registry(默认)
npm config set registry https://registry.npmjs.org/
# 或私有 registry(公司)
npm config set registry https://registry.npmjs.example.com/
国内镜像:https://registry.npmmirror.com(淘宝),但生产构建建议官方源。
5.2 仅装签名包
npm v9+ 支持 --foreground-scripts 显示生命周期脚本。
npm install --ignore-scripts # 跳过 postinstall(最安全但很多包装不上)
6. 检测 typosquatting
提交前检查:
# 看新增依赖
git diff package.json | grep "\"[a-z]"
# 校验包真实性
npm view react versions
npm view react author
npm view react repository.url # 应指向 facebook/react
可疑包名:
- 拼写错误(reactt、loadash)
- 新发布且 download 极少
- author 没历史包
- 没 GitHub repo
- README 抄袭知名包
7. SBOM(软件物料清单)
记录所有依赖供合规:
npm i -g @cyclonedx/cyclonedx-npm
cyclonedx-npm --output-file bom.json
存档每次发布的 SBOM,CVE 公布时能查影响范围。
8. 锁定 npm 行为
.npmrc:
audit=true
fund=false
save-exact=true # 装包不带 ^ ~(精确版本)
package-lock=true
ignore-scripts=false # 视风险定
engine-strict=true # 严格 Node 版本检查
9. CI 安全集成
- name: Audit
run: npm audit --audit-level=high
- name: Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: $}} secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: SBOM
run: |
npx @cyclonedx/cyclonedx-npm --output-file bom.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: bom.json
10. 应急响应
某依赖被曝高危漏洞:
npm ls <pkg>看是不是直接 / 传递依赖- 看官方 advisory 影响版本
- 升级到 fixed version
- 不能升级时考虑:
- 替换包
- npm overrides 强制传递依赖版本:
"overrides": {"vulnerable-pkg": "1.2.3"} - 重新构建 + 部署所有环境
11. 常见反模式
- lockfile 不提交:每次 install 拿不同版本
^~范围依赖不锁:自动升小版本,可能引入漏洞- 不审 audit 输出:CI 即使报警也忽略
- registry 用国内镜像装生产:镜像延迟可能拿到老版本,且镜像本身风险
- 盲目 npm audit fix --force:引入 breaking change 把生产搞挂
- 不验证新依赖:随手装个
cool-pkg没看是谁发的 - 生产装 dev 依赖:扩大攻击面(用
npm ci --omit=dev) - postinstall 不审查:包安装即 RCE
- 私有 registry 凭证写代码:泄露