Why CI/CD Security Matters
Your CI/CD pipeline is a high-value target for attackers. A compromised pipeline can inject malicious code into production, steal secrets, or give attackers persistent access to your infrastructure.
Securing your CI/CD pipeline protects against:
- Supply chain attacks injecting malicious code during build
- Credential theft from exposed secrets in CI logs
- Unauthorized deployments bypassing code review
- Malicious dependencies introduced through package managers
- Data exfiltration through compromised build environments
A secure pipeline ensures that only reviewed, tested code reaches production.
Heroku Pipeline Security
Heroku Pipelines provide a structured deployment workflow: development → staging → production. Each stage should have appropriate security controls.
Pipeline Architecture
[GitHub/GitLab]
↓
[Review Apps] ← Pull Requests
↓
[Staging] ← Automatic deploy from main
↓
[Production] ← Manual promotion
Best Practices
- Require manual promotion to production (no auto-deploy)
- Use separate apps for each pipeline stage
- Different config vars per environment
- Audit who can promote to production
Review App Security
Review Apps automatically create isolated environments for pull requests. While convenient, they introduce security considerations.
Protecting Review Apps
- Use different database than production (never connect to prod data)
- Sanitize test data (no real customer information)
- Set review app TTL (auto-destroy after merge)
- Limit external service access (use test/sandbox APIs)
- Disable indexing (add X-Robots-Tag: noindex)
Review App Config
// app.json
{
"environments": {
"review": {
"env": {
"DATABASE_URL": {
"required": true
},
"STRIPE_SECRET_KEY": {
"value": "sk_test_review_app_key"
}
},
"addons": ["heroku-postgresql:mini"]
}
}
}
Secrets in Review Apps
Never expose production secrets to review apps:
- Use test/sandbox API keys
- Configure separate OAuth apps for review
- Never seed review apps with production data
GitHub Integration Security
Protecting GitHub Connection
- Use GitHub Apps (not OAuth apps) for better permission scoping
- Enable branch protection on main branch
- Require pull request reviews before merge
- Require status checks (tests must pass)
- Require signed commits for verified authorship
Branch Protection Rules
Configure in GitHub repository settings:
- Require pull request before merging
- Require 1+ approving reviews
- Dismiss stale reviews on new commits
- Require status checks to pass
- Require branches to be up to date
- Include administrators in rules
Deploy Keys
If using deploy keys:
- Use read-only keys when possible
- Rotate keys regularly
- Audit key usage
Dependency Scanning
Vulnerable dependencies are a major attack vector. Scan dependencies in CI before deployment.
Automated Scanning Tools
| Tool | Language | Integration |
|---|---|---|
| Dependabot | Multi | GitHub native |
| Snyk | Multi | CI integration |
| npm audit | Node.js | npm CLI |
| bundler-audit | Ruby | Bundler plugin |
| Safety | Python | pip install safety |
CI Integration Example
# GitHub Actions
- name: Security Scan
run: |
npm audit --audit-level=high
# or for Ruby
bundle audit check --update
Handling Vulnerabilities
- Critical/High: Block deployment, fix immediately
- Medium: Fix within sprint
- Low: Track and address in maintenance
Automated Security Testing
Integrate security testing into your CI pipeline.
Static Analysis (SAST)
Scan code for security issues before deployment:
# Example: Brakeman for Rails
- name: Security Scan
run: brakeman --no-pager --exit-on-warn
Dynamic Testing (DAST)
Test running application for vulnerabilities:
- OWASP ZAP
- Burp Suite
- Nikto
Run against staging, not production.
Secret Scanning
Prevent secrets from being committed:
# GitHub Actions - detect secrets
- name: Secret Scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
Deployment Approval Workflows
Require Approvals for Production
Configure Heroku Pipeline to require manual promotion:
- Set production app to manual deployment
- Define who can promote (limit to senior engineers)
- Require testing on staging first
- Document promotion criteria
Two-Person Rule
For sensitive applications, require two-person authorization:
- Developer opens PR
- Reviewer approves and merges
- Second person promotes to production
Audit Trail
Track all deployments:
# View deployment history
heroku releases -a your-production-app
Protecting Build Secrets
CI Environment Security
- Mask secrets in logs (CI systems should auto-mask)
- Use CI secret storage (not in repo files)
- Limit secret scope (only jobs that need them)
- Rotate CI secrets on suspected compromise
- Audit secret access
GitHub Actions Secrets
# Access secrets in workflows
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
Never echo secrets or include them in artifacts.
Protecting Heroku API Keys
- Use Heroku CI variables for Heroku-specific secrets
- Create dedicated CI service accounts
- Use short-lived tokens when possible
Secure Deployment Checklist
Pre-Deployment
- All tests pass (unit, integration, security)
- Code reviewed and approved
- Dependencies scanned for vulnerabilities
- No secrets in code or logs
- Branch protection enforced
Deployment
- Deploy to staging first
- Smoke tests pass on staging
- Manual approval for production
- Deployment logged and auditable
Post-Deployment
- Health checks pass
- Error rates normal
- Performance metrics stable
- Rollback plan ready
Handling Compromised Pipelines
If you suspect pipeline compromise:
- Immediately: Revoke all CI/CD credentials
- Audit: Review recent deployments for malicious changes
- Investigate: Check CI logs for unauthorized access
- Remediate: Rotate all secrets that CI had access to
- Harden: Implement additional controls to prevent recurrence