DevToolBox免费
博客

GitHub Actions 密钥与安全:环境保护、OIDC 认证与最佳实践

12 分钟作者 DevToolBox

GitHub Actions 密钥允许你在仓库设置中安全存储敏感数据——API 密钥、密码、令牌、证书——并在工作流中使用它们,而无需在代码中暴露。本指南涵盖从基本密钥使用到高级模式(如 OIDC 联合认证、环境范围密钥和自动轮换)的所有内容。

基本密钥使用

密钥在仓库设置(Settings > Secrets and variables > Actions)中定义,并在工作流中使用 secrets 上下文引用。它们在工作流日志中自动被遮蔽。

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to server
        env:
          # Reference a repository secret
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          API_TOKEN: ${{ secrets.API_TOKEN }}
        run: |
          echo "Deploying with secure credentials..."
          ./deploy.sh

环境范围的密钥

GitHub Environments 允许你定义特定于部署环境(staging、production)的密钥,并添加保护规则,如必要的审查者。

# Environment-specific secrets (staging vs production)
name: Deploy

on:
  push:
    branches: [main, staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    # Pick environment based on branch
    environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}

    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        env:
          # These come from the selected environment's secrets
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: ./deploy.sh --env ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}

OIDC 联合认证:无需长期密钥

2026 年的最佳实践是使用 OIDC(OpenID Connect)联合认证来与云提供商进行身份验证,而不是存储长期访问密钥。GitHub Actions 可以将 JWT 令牌交换为临时云凭证。

# OIDC: No long-lived credentials needed!
name: Deploy to AWS (OIDC)

on:
  push:
    branches: [main]

permissions:
  id-token: write   # Required for OIDC
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
          aws-region: us-east-1
          # No ACCESS_KEY or SECRET_KEY secrets needed!

      - name: Deploy to S3
        run: aws s3 sync ./dist s3://my-bucket --delete

密钥遮蔽和安全使用

GitHub 自动在日志中遮蔽密钥值,但有一些重要的注意事项需要了解以避免意外暴露。

# Secrets are automatically masked in logs
# But be careful with derived values!

jobs:
  careful:
    runs-on: ubuntu-latest
    steps:
      - name: Use secret safely
        env:
          MY_SECRET: ${{ secrets.MY_SECRET }}
        run: |
          # This output will be masked: ***
          echo "Secret value: $MY_SECRET"

          # DANGER: This may expose the secret!
          # If MY_SECRET="abc", base64 gives "YWJj" which is NOT masked
          echo $MY_SECRET | base64

          # Safe: always use secrets directly, not derived forms
          curl -H "Authorization: Bearer $MY_SECRET" https://api.example.com/endpoint

      - name: Add to mask (for dynamic secrets)
        run: |
          # Manually mask a dynamically generated value
          TOKEN=$(generate-token.sh)
          echo "::add-mask::${TOKEN}"
          echo "TOKEN=${TOKEN}" >> $GITHUB_ENV

可复用工作流中的密钥

使用可复用工作流(workflow_call)时,密钥必须显式传递。它们不会自动继承。

# .github/workflows/reusable-deploy.yml
name: Reusable Deploy Workflow

on:
  workflow_call:
    secrets:
      DEPLOY_KEY:
        required: true
      DATABASE_URL:
        required: true
      SLACK_WEBHOOK:
        required: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: ./deploy.sh

      - name: Notify Slack
        if: ${{ secrets.SLACK_WEBHOOK != '' }}
        run: |
          curl -X POST -H 'Content-type: application/json' \
            --data '{"text":"Deployment complete!"}' \
            ${{ secrets.SLACK_WEBHOOK }}

# .github/workflows/production.yml — caller
name: Production Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    uses: ./.github/workflows/reusable-deploy.yml
    secrets:
      DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
      DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
      SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

自动密钥轮换

长期密钥是安全风险。使用计划工作流设置自动轮换。

# Automated secret rotation pattern
name: Rotate API Token

on:
  schedule:
    - cron: '0 0 1 * *'  # Monthly rotation
  workflow_dispatch:       # Manual trigger

jobs:
  rotate:
    runs-on: ubuntu-latest
    steps:
      - name: Generate new token
        id: generate
        env:
          OLD_TOKEN: ${{ secrets.API_TOKEN }}
        run: |
          # Call API to generate new token using old token
          NEW_TOKEN=$(curl -s -X POST \
            -H "Authorization: Bearer $OLD_TOKEN" \
            https://api.example.com/tokens/rotate | jq -r '.token')
          echo "::add-mask::${NEW_TOKEN}"
          echo "new_token=${NEW_TOKEN}" >> $GITHUB_OUTPUT

      - name: Update GitHub secret
        uses: gliech/create-github-secret-action@v1
        with:
          name: API_TOKEN
          value: ${{ steps.generate.outputs.new_token }}
          github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}

密钥存储选项对比

OptionScopeVisibilityRotationCostBest For
Repository SecretsSingle repoRepo adminsManualFreeSimple projects
Environment SecretsSingle repo + envRepo adminsManualFreeMulti-stage deploys
Organization SecretsMultiple reposOrg adminsManualFreeShared credentials
OIDC FederationSingle repoNo stored secretAutomaticFreeCloud deployments
HashiCorp VaultAnyConfigurableAutomaticPaidEnterprise

安全最佳实践

  1. 尽可能对云提供商(AWS、GCP、Azure)使用 OIDC 联合认证,而不是静态访问密钥。
  2. 将密钥限定在环境范围内。生产密钥应需要环境保护规则(必要的审查者、部署分支)。
  3. 永远不要在工作流步骤中打印密钥,即使是用于调试。GitHub 遮蔽确切的字符串,但编码/转换会绕过遮蔽。
  4. 定期轮换密钥。为 API 密钥设置日历提醒或使用自动轮换工作流。
  5. 使用所需的最小权限。专门为 GitHub Actions 创建具有狭窄范围的服务账户。
  6. 通过 GitHub 的审计日志审计密钥访问。检查哪些工作流在使用哪些密钥。

常见问题

分叉的仓库可以访问密钥吗?

不能。默认情况下,密钥不会传递给由分叉触发的拉取请求工作流。这可以防止分叉仓库窃取你的密钥。对于公共仓库,你可以配置哪些事件允许外部贡献者使用密钥,但这需要明确批准。

仓库密钥和组织密钥有什么区别?

仓库密钥只对该特定仓库中的工作流可用。组织密钥可以在多个仓库之间共享,并有策略设置来控制哪些仓库可以访问它们(所有仓库、选定仓库或仅私有仓库)。

我可以存储多少密钥?

GitHub 允许每个仓库最多 100 个密钥,每个组织最多 1,000 个。每个密钥最大 64 KB。对于较大的密钥(如证书),可以考虑存储 base64 编码版本并在工作流中解码。

存储后我可以读取密钥值吗?

不能。一旦创建密钥,其值就无法通过 GitHub UI 或 API 读取。你只能更新或删除它。这是设计使然——如果你丢失了密钥,创建新的并轮换凭证。

如何在工作流中的作业之间传递密钥?

密钥不能直接作为作业输出在作业之间传递(输出在日志中可见)。相反,在每个需要它的作业中使用 secrets.MY_SECRET 重新引用密钥,或者在一个作业中生成临时凭证,并通过使用 GPG 的加密构件存储传递。

相关工具

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

#Hash GeneratorB→Base64 Encode Online🔐JWT Generator

相关文章

Git 分支策略:GitFlow vs 主干开发 vs GitHub Flow

对比 GitFlow、主干开发和 GitHub Flow 分支策略。学习分支结构、合并工作流、CI/CD 集成,以及如何为你的团队选择合适的策略。

Kubernetes 入门完全教程 (2026)

从零学习 Kubernetes:Pod、Service、Deployment、ConfigMap 等核心概念。