GITHUB_TOKEN 最小权限
默认权限与安全限制
GITHUB_TOKEN 是 GitHub Actions 自动提供的临时令牌,用于与 GitHub API 交互。默认情况下,它具有仓库的读写权限——这比大多数 Job 需要的权限要宽泛。遵循最小权限原则,应在 Workflow 和 Job 级别精确声明所需权限。
# 在 Workflow 顶层声明默认权限(推荐设为只读)
permissions:
contents: read # 全局只读
jobs:
read-only-job:
runs-on: ubuntu-latest
# 继承 workflow 级别的 contents: read
steps:
- uses: actions/checkout@v4
deploy-job:
runs-on: ubuntu-latest
# 覆盖:这个 Job 需要额外权限
permissions:
contents: read
packages: write # 推送到 GHCR
id-token: write # OIDC 认证
steps:
- run: echo "有权限推送镜像"
pr-comment-job:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # 在 PR 中写评论
issues: write
steps:
- run: echo "有权限写 PR 评论"
权限参考表
| 权限名称 | read 允许 | write 允许 |
|---|---|---|
| contents | 读取仓库内容 | 推送代码、创建 Release |
| packages | 下载包 | 发布包到 GHCR |
| pull-requests | 读取 PR 信息 | 创建/修改 PR、添加评论 |
| issues | 读取 Issue | 创建/修改 Issue |
| security-events | 读取安全警报 | 上传 SARIF 扫描结果 |
| id-token | - | 获取 OIDC JWT(云部署) |
| checks | 读取 Check Run | 创建 Check Run |
OIDC:无密钥云部署
为什么 OIDC 比 Access Key 更安全?
传统方式需要将云平台的长期访问密钥(AWS Access Key、GCP Service Account Key)存储为 GitHub Secrets。这些密钥一旦泄露,攻击者可以长期使用。OIDC(OpenID Connect)允许 GitHub Actions 通过临时令牌向云平台证明身份,无需存储任何长期密钥。
# AWS OIDC 配置(需要先在 AWS IAM 中配置 OIDC Provider)
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # 必须!用于获取 OIDC 令牌
contents: read
steps:
- uses: actions/checkout@v4
- name: 配置 AWS 凭证(OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
# IAM Role ARN(在 AWS 中创建并信任 GitHub OIDC Provider)
role-to-assume: arn:aws:iam::123456789:role/github-actions-deploy
# 精确控制:只允许特定仓库和分支使用此 Role
aws-region: us-east-1
# 现在可以直接使用 AWS CLI,无需任何密钥
- run: aws s3 ls
AWS IAM OIDC 配置(Terraform)
# 在 AWS 中创建 OIDC Provider
aws iam create-open-id-connect-provider \
--url "https://token.actions.githubusercontent.com" \
--client-id-list "sts.amazonaws.com" \
--thumbprint-list "6938fd4d98bab03faadb97b34396831e3780aea1"
# 创建 IAM Role(限定只有特定仓库/分支可以 Assume)
# Trust Policy 中的条件:
# "token.actions.githubusercontent.com:sub":
# "repo:your-org/your-repo:ref:refs/heads/main"
CodeQL 代码安全扫描
name: CodeQL Analysis
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * 1' # 每周一凌晨扫描
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read
strategy:
matrix:
language: [javascript, python, go]
# 支持:cpp, csharp, go, java, javascript, python, ruby, swift
steps:
- uses: actions/checkout@v4
- name: 初始化 CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# 使用安全扩展查询集
queries: security-extended
- name: 自动构建(CodeQL 需要)
uses: github/codeql-action/autobuild@v3
- name: 执行 CodeQL 分析
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
Trivy 容器漏洞扫描
jobs:
trivy-scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: 构建镜像
run: docker build -t my-app:${{ github.sha }} .
# 扫描容器镜像
- name: Trivy 漏洞扫描
uses: aquasecurity/trivy-action@master
with:
image-ref: my-app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: 1 # 发现高危漏洞时 CI 失败
# 上传扫描结果到 GitHub Security Tab
- name: 上传 Trivy 结果
uses: github/codeql-action/upload-sarif@v3
if: always() # 即使 trivy 扫描失败也上传结果
with:
sarif_file: trivy-results.sarif
# 同时扫描代码依赖(SCA)
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Trivy 文件系统扫描
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
scan-ref: .
format: table
severity: CRITICAL,HIGH
依赖安全:Dependabot 自动更新
# .github/dependabot.yml — 配置自动依赖更新
version: 2
updates:
# npm 依赖更新
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
time: "09:00"
# 将安全更新与常规更新分组(减少 PR 数量)
groups:
production-dependencies:
dependency-type: production
development-dependencies:
dependency-type: development
# 忽略主版本更新(可能有破坏性变更)
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
# GitHub Actions 版本更新(包括固定 SHA 的更新)
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
# 自动合并补丁和小版本更新
reviewers:
- "platform-team"
供应链安全:固定 Action 版本到 SHA
不要使用 uses: actions/checkout@main 或 @v4 标签(标签可以被恶意覆盖)。应固定到具体 commit SHA:uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683(即 v4.2.2 对应的 SHA)。配合 Dependabot 自动更新这些 SHA,既安全又省心。这是防止供应链攻击的关键实践。
本章小结
本章核心要点
- 最小权限原则:Workflow 顶层设置 permissions: contents: read(只读),在具体 Job 中按需开启额外权限;防止一个 Job 被攻击后影响整个仓库。
- OIDC 无密钥部署:GitHub Actions 通过 OIDC JWT 向云平台(AWS/GCP/Azure)证明身份,获取临时凭证;无需在 Secrets 中存储长期 Access Key,减少凭证泄露风险。
- CodeQL 代码扫描:GitHub 免费提供,支持多语言(JS/Python/Go/Java/C++ 等);定期扫描(cron)+ PR 扫描双重保障;结果上传到 Security Tab,在代码级别标记漏洞位置。
- Trivy 容器扫描:扫描容器镜像中的 OS 包漏洞和应用依赖漏洞;SARIF 格式上传 GitHub Security Tab;exit-code: 1 在发现高危漏洞时中断 CI 流水线。
- 固定 Action SHA:使用具体 commit SHA 而非版本标签引用 Action,防止标签被恶意覆盖的供应链攻击;Dependabot github-actions 生态系统自动维护 SHA 更新。