学习 Github Actions 如何安全管理敏感信息

4 阅读5分钟

为什么需要 Secrets?

直接在 YAML 文件中存储敏感信息会导致:

  • 代码泄露风险:即使是私有仓库,也可能因疏忽被公开。
  • 权限失控:所有有权限查看代码的人都能获取敏感信息。
  • 合规问题:违反数据保护法规(如 GDPR)。
  • 密钥滥用:一旦泄露,可能被恶意使用。

如何创建和管理 Secrets?

  1. 在仓库中创建 Secrets

    • 进入仓库的 Settings → Secrets and variables → Actions
    • 点击 New repository secret
    • 输入 Name(全大写,如 AWS_ACCESS_KEY_ID)和 Value(实际密钥)
    • 点击 Add secret
  2. 在组织级别创建 Secrets

    • 适用于多个仓库共享的密钥
    • 进入组织的 Settings → Secrets and variables → Actions
    • 点击 New organization secret
    • 设置访问权限(可限制特定仓库使用)
  3. 环境级别 Secrets

    • 用于不同环境(如开发、测试、生产)的差异化配置
    • 在仓库的 Settings → Environments 中创建环境
    • 为环境添加 Secrets

如何在工作流中使用 Secrets?

Secrets 通过 ${{ secrets.SECRET_NAME }} 语法注入到工作流中

高级用法:条件性访问和动态注入

  1. 基于环境的条件访问
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # 指定环境
    
    steps:
      - name: 部署到生产环境
        env:
          # 使用生产环境的 Secrets
          PROD_API_KEY: ${{ secrets.PROD_API_KEY }}
        run: ./deploy.sh --env=production
  1. 矩阵构建中的 Secrets
jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        env: [staging, production]
    
    steps:
      - name: 根据矩阵环境选择 Secrets
        env:
          # 动态选择 Secrets(需预先创建 STAGING_API_KEY 和 PROD_API_KEY)
          API_KEY: ${{ secrets[format('{0}_API_KEY', matrix.env)] }}
        run: ./deploy.sh --env=${{ matrix.env }}

使用 Secrets 的安全注意事项

在 GitHub Actions 的 YAML 文件中使用 Secrets 时,必须特别注意安全细节,以避免敏感信息泄露。以下是详细的注意事项和最佳实践:

1. 避免在 YAML 中硬编码敏感信息

错误示例

run: |
  curl -u "username:password123" https://api.example.com  # ❌ 直接硬编码密码
  aws s3 sync ./dist s3://my-bucket --access-key ABC123 --secret-key DEF456  # ❌ 硬编码 AWS 密钥

正确做法

env:
  API_KEY: ${{ secrets.API_KEY }}
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

run: |
  curl -u "username:$API_KEY" https://api.example.com  # ✅ 使用环境变量
  aws s3 sync ./dist s3://my-bucket  # ✅ AWS CLI 自动读取环境变量

2. 防止 Secrets 意外暴露在日志中

GitHub 会自动屏蔽日志中的 Secrets 值,但仍需注意:

风险场景

# 错误示例:直接打印 Secrets
run: echo "API_KEY=${{ secrets.API_KEY }}"  # ❌ 即使被屏蔽,仍会在日志中显示 `***`

# 正确示例:通过环境变量间接使用
env:
  API_KEY: ${{ secrets.API_KEY }}
run: |
  echo "使用 API_KEY 进行操作..."  # ✅ 不直接打印 Secrets
  curl -H "Authorization: Bearer $API_KEY" https://api.example.com

3. 限制第三方 Actions 的 Secrets 访问

风险示例

- uses: third-party-action@v1
  with:
    token: ${{ secrets.GITHUB_TOKEN }}  # ❌ 授予第三方完全访问权限
    secret: ${{ secrets.SOME_SECRET }}  # ❌ 传递敏感信息给不可信代码

安全做法

  • 最小权限原则:仅使用必要的 Secrets。
  • 验证 Actions 来源:优先使用官方或知名维护者的 Actions。
  • 审查 Actions 代码:检查开源 Actions 的实现,确保不滥用 Secrets。

4. 处理 Pull Request 中的 Secrets 访问

风险:来自外部贡献者的 PR 默认无法访问仓库 Secrets,否则可能导致攻击(如窃取密钥)。

错误示例

on:
  pull_request:  # ❌ 直接从 PR 触发敏感操作
    branches: [main]
jobs:
  build:
    steps:
      - name: 部署到生产环境
        env:
          PROD_API_KEY: ${{ secrets.PROD_API_KEY }}  # ❌ 危险!PR 可能来自恶意分支
        run: ./deploy.sh  # ❌ 直接部署到生产环境

安全做法

  1. 仅在合并后触发

    on:
      push:  # ✅ 仅在代码合并到主分支后触发
        branches: [main]
    
  2. 使用 pull_request_target 但添加防护

    on:
      pull_request_target:  # ✅ 在基础分支(非 PR 分支)上执行
        types: [opened, synchronize, reopened]
    jobs:
      build:
        if: github.event.pull_request.head.repo.full_name == github.repository  # ✅ 仅允许内部 PR
        # 后续步骤...
    

5. 避免通过命令行参数传递 Secrets

风险示例

run: ./script.sh --password=${{ secrets.DB_PASSWORD }}  # ❌ 命令行参数可能被日志捕获

安全做法

env:
  DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
run: ./script.sh  # ✅ 脚本从环境变量读取密码

6. 合理设置 Secrets 的访问范围

  • 仓库级 Secrets:适用于单个仓库。
  • 组织级 Secrets:适用于多个仓库,但需限制访问范围(如特定仓库或环境)。
  • 环境级 Secrets:按环境隔离(如开发 / 测试 / 生产)。

错误示例

# 在组织中创建了一个万能的 AWS_ADMIN 密钥,所有仓库可无限制访问 ❌

正确做法

  1. 组织级 Secrets
    # 组织设置 → Secrets → Actions → 新建
    # 名称:AWS_READONLY_KEY
    # 范围:限制为特定仓库组
    
  2. 环境级 Secrets
    # 仓库设置 → Environments → 新建环境(如 "production"
    # 为环境添加 Secrets(仅该环境可访问)
    

7. 定期轮换和审计 Secrets

  • 轮换周期:每 90 天或更短(取决于安全要求)。
  • 审计日志:检查 GitHub 组织的审计日志,监控 Secrets 的创建、修改和访问。

自动化轮换示例

on:
  schedule:
    - cron: '0 0 1 * *'  # 每月 1 日执行

jobs:
  rotate-secrets:
    runs-on: ubuntu-latest
    steps:
      - name: 生成新密钥
        run: |
          NEW_KEY=$(openssl rand -base64 32)
          echo "NEW_KEY=$NEW_KEY" >> $GITHUB_ENV
      
      - name: 更新 Secrets(通过 GitHub API)
        env:
          GITHUB_TOKEN: ${{ secrets.PAT_WITH_SECRET_WRITE_ACCESS }}  # 需要 Personal Access Token
        run: |
          curl -X PUT \
            -H "Authorization: token $GITHUB_TOKEN" \
            -d "{"encrypted_value":"$(./encrypt.sh $NEW_KEY)","key_id":"$KEY_ID"}" \
            https://api.github.com/repos/$ORG/$REPO/actions/secrets/MY_SECRET

8. 避免在公共仓库中使用敏感 Secrets

  • 风险:即使 Secrets 在日志中被屏蔽,恶意用户仍可能通过构造特殊的工作流绕过防护。

  • 替代方案

    • 使用公共仓库的非敏感功能(如构建文档)。
    • 将敏感操作迁移到私有仓库。

9. 验证 Secrets 的值是否为空

风险示例

run: |
  # 如果 API_KEY 为空,命令可能默认使用生产环境的凭证
  curl -H "Authorization: Bearer $API_KEY" https://api.example.com

安全做法

run: |
  if [ -z "$API_KEY" ]; then
    echo "错误:API_KEY 为空"
    exit 1
  fi
  curl -H "Authorization: Bearer $API_KEY" https://api.example.com

10. 使用环境变量文件(.env)管理本地开发配置

错误示例

# 本地开发时直接使用生产密钥
env:
  DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}  # ❌ 开发环境不应使用生产密钥

正确做法

  1. 创建 .env 文件(添加到 .gitignore):

    bash

    # .env
    DB_PASSWORD=dev_password
    API_KEY=dev_api_key
    
  2. 在工作流中使用

    yaml

    - name: 设置环境变量
      run: |
        echo "DB_PASSWORD=dev_password" >> $GITHUB_ENV
        echo "API_KEY=dev_api_key" >> $GITHUB_ENV
    

安全最佳实践总结

  1. 最小权限原则

    • 为每个 Secrets 分配完成任务所需的最小权限
    • 例如,AWS IAM 用户仅授予 S3 读写权限,而非管理员权限
  2. 定期轮换密钥

    • 设置密钥轮换计划(如每 90 天)
    • 使用 GitHub Actions 自动化轮换过程
  3. 审计和监控

    • 定期检查 Secrets 使用情况
    • 启用 GitHub 组织的审计日志
  4. 避免日志泄露

    • GitHub 会自动屏蔽日志中的 Secrets 值
    • 但仍需注意通过其他方式泄露(如错误输出、调试信息)
  5. 使用环境隔离

    • 为不同环境(开发 / 测试 / 生产)创建独立的 Secrets
    • 避免在开发环境使用生产密钥

常见问题与解决方案

  1. Secrets 未生效

    • 检查名称拼写(区分大小写)
    • 确认 Secrets 已正确添加到仓库或组织
    • 对于组织级 Secrets,确保仓库有权限访问
  2. Secrets 在 PR 中不可用

    • 来自外部贡献者的 PR 默认无法访问仓库 Secrets

    • 解决方案:

      • 使用 pull_request_target 事件(但需注意安全风险)
      • 在合并后执行敏感操作
  3. 需要访问第三方服务的 API

    • 创建服务专用的 API 密钥,而非使用个人账号
    • 考虑使用 OAuth 令牌代替静态密钥
  4. 本地开发如何使用 Secrets

    • 使用环境变量文件(如 .env

    • 使用工具如 direnv 自动加载环境变量

    • 切勿将 .env 文件提交到版本控制

通过合理使用 GitHub Secrets,你可以在保持工作流自动化的同时,有效保护敏感信息的安全。