- Pin third-party actions by full SHA, not tags: uses: actions/checkout@abc123... (tags can be moved)
- Set minimal GITHUB_TOKEN permissions at workflow level: permissions: { contents: read }
- Never echo or log secrets — use masking (::add-mask::) and avoid passing secrets as command arguments
- Use environment protection rules for production deployments: required reviewers and deployment branches
- Store secrets in GitHub Secrets (repo or org level), never in workflow YAML files or repository code
- Pin actions by SHA: uses: actions/checkout@11bd71901... — prevent supply chain attacks from tag mutation
- Minimal GITHUB_TOKEN permissions: set at workflow level, override per job if needed; default to read-only
- Never log secrets: avoid echo $SECRET, use ::add-mask:: for dynamic values, don't pass secrets as CLI args
- Environment protection: required reviewers, wait timer, deployment branch restrictions for production
- Secrets storage: GitHub Secrets for CI values, OIDC for cloud provider authentication (no long-lived keys)
- Use OIDC (OpenID Connect) for AWS/Azure/GCP: short-lived tokens instead of stored access keys
- Restrict workflow triggers: avoid pull_request_target with checkout of PR code (code injection risk)
- Use Dependabot or Renovate to keep action versions updated with automatic PRs for security patches
- Set concurrency groups to prevent parallel runs of sensitive workflows: concurrency: { group: deploy-prod }
- Audit workflow runs: review Actions usage, check for unexpected workflows, monitor for secrets in logs
- Use GitHub's built-in CodeQL and dependency review actions for automated security analysis