CI/CD 管道是供应链攻击的主要目标。源码、构建环境、密钥一旦泄露,攻击者可以直接植入恶意代码、窃取数据或劫持部署。本文从权限配置到认证机制,构建 GitHub Actions 的完整安全防线。
一、最小权限原则(Principle of Least Privilege)
1.1 默认关闭权限,按需分配
# ❌ 危险:默认赋予所有权限
# (不设置 permissions 时,默认读写所有内容)
# ✅ 推荐:显式声明最小权限
permissions:
contents: read # 只读代码
pull-requests: write # 仅 PR 操作
id-token: write # OIDC Token(云认证)
jobs:
deploy:
permissions:
contents: read
packages: write # 推送容器镜像
id-token: write
权限级别:
| 级别 | 作用域 | 说明 |
|---|---|---|
read | 只读 | safest,默认首选 |
write | 读写 | 需要修改资源时使用 |
none | 无权限 | 显式禁用 |
1.2 组织级权限策略
# 在组织级别限制默认权限
# GitHub Organization → Settings → Actions → General
# Default workflow permissions: Read repository contents and packages permissions
二、Secrets 管理策略
2.1 Secret vs Variable:明确区分
| 类型 | 存储方式 | 日志显示 | 适用 |
|---|---|---|---|
| Secrets | 加密存储 | ❌ 隐藏(显示为 ***) | API Key、Token、密码 |
| Variables | 明文存储 | ✅ 可见 | 环境名、Region、版本号 |
2.2 Secret 注入正确方式
# ✅ 正确:env 注入,避免命令行回显
jobs:
deploy:
runs-on: ubuntu-latest
env:
API_KEY: ${{ secrets.API_KEY }}
steps:
- run: |
curl -H "Authorization: Bearer $API_KEY" https://api.example.com
# ❌ 危险:直接在命令行使用(部分 CI 系统会回显)
# - run: curl -H "Authorization: Bearer ${{ secrets.API_KEY }}" ...
2.3 Secret 轮转策略
| Secret 类型 | 轮转周期 | 方式 |
|---|---|---|
| 云平台 Access Key | 90 天 | IAM 控制台重新生成 |
| npm Token | 180 天 | npm 网站重新生成 |
| Docker Hub Token | 90 天 | Account Settings → Security |
| SSH 密钥 | 180 天 | ssh-keygen 重新生成 |
三、OIDC 免密云认证
3.1 为什么用 OIDC?
传统方式将云服务密钥(AWS Access Key)存为 Secrets,存在泄露风险。OIDC(OpenID Connect)让 GitHub Actions 通过 JWT Token 临时换取云服务的访问凭证,无需长期密钥。
3.2 AWS OIDC 配置
permissions:
id-token: write
contents: read
jobs:
deploy-aws:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
aws-region: us-east-1
role-duration-seconds: 1200 # 临时凭证有效期
- run: aws s3 sync ./dist s3://my-bucket
AWS IAM 信任策略:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" },
"StringLike": { "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*" }
}
}]
}
3.3 Azure OIDC
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
3.4 GCP OIDC
- name: GCP Auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/123/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: 'github-actions@my-project.iam.gserviceaccount.com'
四、依赖与供应链安全
4.1 Dependabot 自动化依赖更新
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
reviewers:
- "octocat"
4.2 CodeQL 安全扫描
name: CodeQL
on:
push: { branches: [main] }
pull_request: { branches: [main] }
schedule: [ cron: '0 9 * * 1' ]
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript, typescript
- uses: github/codeql-action/analyze@v3
4.3 npm audit 在 CI 中
- run: npm audit --audit-level=moderate
- run: npm audit fix --dry-run # 查看建议修复
4.4 SLSA 来源证明
- uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
with:
base64-subjects: "${{ hashFiles('dist/**') }}"
五、Self-hosted Runner 安全
5.1 Runner 隔离策略
| 隔离级别 | 方法 | 效果 |
|---|---|---|
| 用户隔离 | 每个 runner 独立用户 | 防止跨项目访问 |
| 容器隔离 | Docker-in-Docker | 每个 job 独立容器 |
| VM 隔离 | 每次启动新 VM | 最强隔离,启动较慢 |
| 临时 Runner | 用完即销毁 | 最高安全 |
5.2 Runner 标签策略
jobs:
deploy-production:
runs-on: [self-hosted, production] # 标签限制
六、常见安全漏洞与防护
| 漏洞 | 原因 | 防护 |
|---|---|---|
| 秘密泄露 | 硬编码 API Key | 使用 Secrets + OIDC |
| 恶意 Action | 使用未审核第三方 Action | 固定 commit SHA,pin 版本 |
| 脚本注入 | ${{ github.head_ref }} 在命令行中使用 | env 注入,避免字符串拼接 |
| 权限提升 | workflow 具有过高权限 | permissions: contents: read |
| Artifact 泄露 | 敏感文件包含在 Artifact 中 | .gitignore + artifact 路径限制 |
七、安全审计检查清单
- 组织级别设置默认只读权限
- 所有工作流显式声明
permissions - Secrets 使用
env:注入而非命令行 - 使用 OIDC 替代长期云密钥
- 第三方 Action 固定到 commit SHA
- Dependabot 启用自动更新
- CodeQL 启用安全扫描
- npm audit 在 CI 中阻塞高危漏洞
- Self-hosted Runner 启用标签隔离
- 定期轮转 Secrets(90 天)
相关阅读
- GitHub Actions 详解 — 核心概念与语法
- GitHub Actions Docker CI/CD — Trivy 镜像扫描
- GitHub Actions Node.js CI — npm audit 集成
- GitHub Actions 专题导航
- Node.js 专题 — 依赖安全最佳实践
- Docker 专题 — 容器安全扫描
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。
「saas」更多文章
自定义域名接入 Cloudflare:CDN 加速与缓存规则配置实战
手把手讲解自定义域名接入 Cloudflare CDN 的完整流程:NS 接管、SSL/TLS 模式选择、橙云代理与灰云 DNS 的区别,并用 2024 年后的新 Cache Rules 引擎实战配置静态资源长缓存、HTML 不缓存、后台 Bypass,让边缘缓存命中率最大化。
Hugo 部署到 Cloudflare Pages 实战:从 Git 推送到全球边缘上线
详解 Cloudflare Pages 部署 Hugo 静态站的完整流程,涵盖框架预设、HUGO_VERSION 环境变量固定、构建命令、自定义域名、分支预览、_headers 缓存安全头配置及常见构建失败排查,一次推送即可全球边缘上线。
Cloudflare Workers 入门实战:在边缘运行你的第一行代码
一份面向初学者的 cloudflare workers 教程,从 V8 Isolate 运行时模型讲起,手把手演示 wrangler 初始化、路由分发、KV 存储、Cache API 缓存响应与环境变量配置,帮你在 30 分钟内部署第一个生产可用的边缘 Serverless 应用。