← Back to Home

Secrets in CI/CD Pipelines — Stop Leaking Credentials

How to handle secrets in CI/CD pipelines securely. OIDC federation, runtime secret injection, Vault integration, and the anti-patterns that lead to credential leaks in build systems.

Your CI/CD pipeline has more access than any single developer. It can deploy to production, access databases, push to registries, and modify infrastructure. Yet most teams treat pipeline credentials as an afterthought — static API keys stored in CI environment variables that haven’t been rotated since the pipeline was created. If an attacker compromises your CI system, they own everything it can access.

Safe vs Dangerous Patterns

The difference between secure and insecure pipeline secrets management isn’t complexity — it’s awareness. Most dangerous patterns are defaults that teams never revisit.

CI/CD Secrets — Safe vs Dangerous Patterns

❌ Anti-Patterns
Secrets hardcoded in Dockerfiles or source code
Secrets in environment variables logged to CI output
Shared service accounts with no rotation
Secrets copy-pasted between pipeline configs
✅ Best Practices
Pull secrets at runtime from Vault/AWS SM/GCP SM
Short-lived OIDC tokens for cloud auth (no static keys)
Masked/redacted secrets in CI logs automatically
Least-privilege: each pipeline gets only needed secrets

The most common leak vector isn’t sophisticated attacks — it’s accidental log exposure. A debug statement prints environment variables. A failed deployment logs the connection string. A developer adds set -x to troubleshoot a shell script and every variable expansion appears in CI logs. By the time anyone notices, the logs have been visible to everyone with CI access for months.

OIDC Federation: Kill Static Keys

The single most impactful change you can make: replace static cloud credentials with OIDC federation. GitHub Actions, GitLab CI, and CircleCI all support OIDC tokens. Instead of storing AWS access keys in CI variables, the CI provider issues a signed JWT that AWS trusts directly. No static secrets to leak, rotate, or manage.

AWS IAM role trust policies specify which repositories and branches can assume the role. A GitHub Actions workflow gets a short-lived token (default 1 hour) scoped to exactly the permissions that pipeline needs. If the token leaks, it expires automatically. If someone forks the repo, the trust policy doesn’t match and the token request fails.

Setting this up takes 30 minutes per cloud provider. It eliminates the entire category of “leaked CI cloud credentials” vulnerabilities. If your CI pipelines still use static AWS/GCP/Azure keys, migrating to OIDC should be your top priority.

Runtime Secret Injection

Secrets should never exist in pipeline configuration. Not in YAML files, not in Dockerfiles, not in environment variable definitions that are committed to source control. Secrets belong in a secrets manager (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager) and get injected at runtime.

The pattern: your pipeline authenticates to the secrets manager (via OIDC ideally), fetches the secrets it needs for this specific run, uses them, and the secrets only exist in process memory. They’re never written to disk, never logged, and never persisted in CI artifacts.

For Docker builds, use multi-stage builds with --secret mounts. The secret is available during the build step but never written to any layer. Alternatively, fetch secrets at container startup rather than bake them into the image. An image with embedded secrets is a liability every time it’s stored, transferred, or inspected.

Least Privilege in Pipelines

Each pipeline stage should have access to only the secrets it needs. The build stage needs registry credentials but not database passwords. The deploy stage needs deploy keys but not build dependencies. Yet most teams use a single set of “CI secrets” available to every pipeline stage.

In GitHub Actions, use environments with required reviewers and environment-specific secrets. Production secrets are only available in workflows that have been approved for the production environment. Feature branch builds can’t access production credentials even if a malicious PR tries.

Audit your CI secret access regularly. List every secret, who created it, when it was last rotated, and which pipelines use it. Stale secrets from decommissioned services, test credentials with production access, and secrets nobody remembers creating are all common findings. Delete what you don’t need.

Detecting Secret Leaks

Prevention fails eventually. You need detection. Pre-commit hooks using tools like gitleaks and trufflehog scan for secret patterns before code leaves the developer’s machine. CI-side scanning catches secrets that slip past local hooks. Post-commit monitoring watches for secrets in public repositories and internal logs.

GitHub’s secret scanning automatically detects tokens from major cloud providers and notifies both the developer and the token issuer. GitLab has similar capabilities. Enable these features — they’re free and catch secrets within minutes of being committed.

When a secret leaks, rotate immediately. Don’t assess the risk first, don’t file a ticket, don’t wait for the security team’s opinion. Rotate the credential, verify the new one works, and then investigate the leak. The window between leak and rotation is the window an attacker has to exploit it. Make that window as small as humanly possible.