为什么需要 Secrets?
直接在 YAML 文件中存储敏感信息会导致:
- 代码泄露风险:即使是私有仓库,也可能因疏忽被公开。
- 权限失控:所有有权限查看代码的人都能获取敏感信息。
- 合规问题:违反数据保护法规(如 GDPR)。
- 密钥滥用:一旦泄露,可能被恶意使用。
如何创建和管理 Secrets?
-
在仓库中创建 Secrets:
- 进入仓库的
Settings
→Secrets and variables
→Actions
- 点击
New repository secret
- 输入
Name
(全大写,如AWS_ACCESS_KEY_ID
)和Value
(实际密钥) - 点击
Add secret
- 进入仓库的
-
在组织级别创建 Secrets:
- 适用于多个仓库共享的密钥
- 进入组织的
Settings
→Secrets and variables
→Actions
- 点击
New organization secret
- 设置访问权限(可限制特定仓库使用)
-
环境级别 Secrets:
- 用于不同环境(如开发、测试、生产)的差异化配置
- 在仓库的
Settings
→Environments
中创建环境 - 为环境添加 Secrets
如何在工作流中使用 Secrets?
Secrets 通过 ${{ secrets.SECRET_NAME }}
语法注入到工作流中
高级用法:条件性访问和动态注入
- 基于环境的条件访问:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # 指定环境
steps:
- name: 部署到生产环境
env:
# 使用生产环境的 Secrets
PROD_API_KEY: ${{ secrets.PROD_API_KEY }}
run: ./deploy.sh --env=production
- 矩阵构建中的 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 # ❌ 直接部署到生产环境
安全做法:
-
仅在合并后触发:
on: push: # ✅ 仅在代码合并到主分支后触发 branches: [main]
-
使用
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 密钥,所有仓库可无限制访问 ❌
正确做法:
- 组织级 Secrets:
# 组织设置 → Secrets → Actions → 新建 # 名称:AWS_READONLY_KEY # 范围:限制为特定仓库组
- 环境级 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 }} # ❌ 开发环境不应使用生产密钥
正确做法:
-
创建
.env
文件(添加到.gitignore
):bash
# .env DB_PASSWORD=dev_password API_KEY=dev_api_key
-
在工作流中使用:
yaml
- name: 设置环境变量 run: | echo "DB_PASSWORD=dev_password" >> $GITHUB_ENV echo "API_KEY=dev_api_key" >> $GITHUB_ENV
安全最佳实践总结
-
最小权限原则:
- 为每个 Secrets 分配完成任务所需的最小权限
- 例如,AWS IAM 用户仅授予 S3 读写权限,而非管理员权限
-
定期轮换密钥:
- 设置密钥轮换计划(如每 90 天)
- 使用 GitHub Actions 自动化轮换过程
-
审计和监控:
- 定期检查 Secrets 使用情况
- 启用 GitHub 组织的审计日志
-
避免日志泄露:
- GitHub 会自动屏蔽日志中的 Secrets 值
- 但仍需注意通过其他方式泄露(如错误输出、调试信息)
-
使用环境隔离:
- 为不同环境(开发 / 测试 / 生产)创建独立的 Secrets
- 避免在开发环境使用生产密钥
常见问题与解决方案
-
Secrets 未生效:
- 检查名称拼写(区分大小写)
- 确认 Secrets 已正确添加到仓库或组织
- 对于组织级 Secrets,确保仓库有权限访问
-
Secrets 在 PR 中不可用:
-
来自外部贡献者的 PR 默认无法访问仓库 Secrets
-
解决方案:
- 使用
pull_request_target
事件(但需注意安全风险) - 在合并后执行敏感操作
- 使用
-
-
需要访问第三方服务的 API:
- 创建服务专用的 API 密钥,而非使用个人账号
- 考虑使用 OAuth 令牌代替静态密钥
-
本地开发如何使用 Secrets:
-
使用环境变量文件(如
.env
) -
使用工具如 direnv 自动加载环境变量
-
切勿将
.env
文件提交到版本控制
-
通过合理使用 GitHub Secrets,你可以在保持工作流自动化的同时,有效保护敏感信息的安全。