Skip to content

Split View: DevSecOps 완전 가이드 2025: Shift-Left 보안, SAST/DAST/SCA, 컨테이너 보안까지

✨ Learn with Quiz
|

DevSecOps 완전 가이드 2025: Shift-Left 보안, SAST/DAST/SCA, 컨테이너 보안까지

TL;DR

  • Shift-Left 보안: 개발 초기에 보안을 적용하면 결함 수정 비용이 30배 절감
  • SAST: SonarQube, Semgrep, CodeQL로 코드 레벨 취약점 탐지
  • DAST: OWASP ZAP, Burp Suite로 런타임 취약점 발견
  • SCA: Snyk, Dependabot로 의존성 취약점 자동 패치
  • 컨테이너 보안: Trivy, Grype로 이미지 스캔, 최소 베이스 이미지 사용
  • 공급망 보안: SBOM 생성, SLSA 레벨 달성, Sigstore 서명
  • Secret 탐지: GitLeaks, TruffleHog로 커밋된 비밀정보 탐지
  • GitHub Actions: 통합 보안 파이프라인으로 모든 단계 자동화

목차

  1. DevSecOps란 무엇인가
  2. Shift-Left 보안 전략
  3. SAST - 정적 분석
  4. DAST - 동적 분석
  5. SCA - 소프트웨어 구성 분석
  6. 컨테이너 보안
  7. Secret 탐지
  8. 공급망 보안 - SBOM과 SLSA
  9. Policy-as-Code
  10. GitHub Actions 통합 보안 파이프라인
  11. 실전 퀴즈
  12. 참고 자료

1. DevSecOps란 무엇인가

1.1 전통적 보안의 한계

전통적인 소프트웨어 개발에서 보안은 마지막 단계에 추가되었습니다. 개발이 끝나고 배포 직전에 보안 팀이 검토하는 방식이었죠. 이 접근법의 문제점은 명확합니다.

문제영향
늦은 발견수정 비용 30배 증가
병목 현상보안 팀이 모든 프로젝트 검토
개발자 무관심"보안은 보안 팀의 일" 마인드
릴리즈 지연취약점 발견 시 일정 전체 지연
컨텍스트 부족보안 팀이 코드 의도를 모름

1.2 DevSecOps의 핵심 원칙

DevSecOps는 Development + Security + Operations의 합성어로, 보안을 소프트웨어 개발 생명 주기(SDLC) 전체에 통합하는 문화이자 실천 방법입니다.

핵심 원칙 5가지:

  1. 모든 사람이 보안에 책임: 보안은 보안 팀만의 일이 아님
  2. 자동화 우선: 수동 검토 대신 자동화된 도구 활용
  3. 빠른 피드백: 개발자가 코드를 작성하는 시점에 보안 피드백
  4. 지속적 개선: 보안 메트릭 측정과 지속적인 개선
  5. 공유 책임: 개발, 운영, 보안 팀 간 지식 공유

1.3 DevSecOps 도구 생태계 개요

┌─────────────────────────────────────────────────────────────┐
DevSecOps 파이프라인                        │
├──────────┬──────────┬──────────┬──────────┬────────────────┤
PlanCodeBuildTestDeploy/Run├──────────┼──────────┼──────────┼──────────┼────────────────┤
ThreatSASTSCADASTRuntimeModelingSemgrepSnykZAPProtection│          │ CodeQLTrivyBurpFalcoSecuritySonarQube│ OSVNucleiContainerRequire  │          │          │          │ Security│ ments    │ SecretSBOMFuzzingNetwork│          │ DetectSigning  │          │ Policy├──────────┼──────────┼──────────┼──────────┼────────────────┤
Policy-as-Code (OPA / Kyverno / Conftest)└─────────────────────────────────────────────────────────────┘

2. Shift-Left 보안 전략

2.1 Shift-Left이란?

Shift-Left은 보안 활동을 개발 프로세스의 왼쪽(초기 단계)으로 이동시키는 전략입니다.

전통적 방식:
PlanCodeBuildTestDeploy[보안 검토]Release
                                         ↑ 여기서 발견 = 비용 높음

Shift-Left 방식:
Plan[보안]Code[보안]Build[보안]TestDeploy
  ↑ 여기서 시작 = 비용 낮음

2.2 Shift-Left ROI 분석

IBM Systems Sciences Institute의 연구에 따르면, 결함 수정 비용은 발견 시점에 따라 기하급수적으로 증가합니다.

발견 단계상대 비용예시
요구사항 정의1x보안 요구사항 누락 발견
설계5x아키텍처 취약점 발견
구현10x코드 레벨 취약점 발견
테스트20xQA 단계에서 발견
운영30x프로덕션에서 발견
사고 발생 후100x+데이터 유출 사고

2.3 Shift-Left 구현 단계

단계 1: 보안 챔피언 프로그램

각 개발 팀에 보안 챔피언을 지정합니다.

# security-champions.yaml
teams:
  - name: "Backend Team"
    champion: "alice@company.com"
    responsibilities:
      - "PR 보안 리뷰"
      - "SAST 결과 트리아지"
      - "월간 보안 교육 진행"
    training:
      - "OWASP Top 10"
      - "Secure Coding Practices"
      - "Threat Modeling 기초"
  - name: "Frontend Team"
    champion: "bob@company.com"
    responsibilities:
      - "XSS/CSRF 방지 검토"
      - "의존성 취약점 관리"
      - "CSP 헤더 관리"

단계 2: 위협 모델링

STRIDE 모델을 활용한 위협 모델링을 설계 단계에서 수행합니다.

STRIDE 위협 모델:
┌─────────────────────────────────────────┐
S - Spoofing (위장)T - Tampering (변조)R - Repudiation (부인)I - Information Disclosure (정보 노출)D - Denial of Service (서비스 거부)E - Elevation of Privilege (권한 상승)└─────────────────────────────────────────┘

단계 3: IDE 보안 플러그인

개발자의 IDE에서 바로 보안 피드백을 제공합니다.

// .vscode/settings.json
{
  "semgrep.scan.autoScan": true,
  "semgrep.scan.configuration": [
    "p/owasp-top-ten",
    "p/typescript",
    "p/react"
  ],
  "sonarlint.connectedMode.project": {
    "connectionId": "my-sonarqube",
    "projectKey": "my-project"
  }
}

3. SAST - 정적 분석

3.1 SAST란?

SAST(Static Application Security Testing)는 소스 코드, 바이트코드, 바이너리를 실행하지 않고 분석하여 보안 취약점을 찾는 기법입니다.

SAST가 탐지하는 주요 취약점:

  • SQL Injection
  • Cross-Site Scripting (XSS)
  • 경로 탐색(Path Traversal)
  • 하드코딩된 비밀정보
  • 안전하지 않은 암호화
  • 버퍼 오버플로우

3.2 SonarQube 설정

# docker-compose.yml - SonarQube 로컬 설정
version: "3.8"
services:
  sonarqube:
    image: sonarqube:community
    ports:
      - "9000:9000"
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonar_data:/opt/sonarqube/data
      - sonar_logs:/opt/sonarqube/logs
      - sonar_extensions:/opt/sonarqube/extensions
    depends_on:
      - db
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: sonarqube
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  sonar_data:
  sonar_logs:
  sonar_extensions:
  postgres_data:
# sonar-project.properties
sonar.projectKey=my-app
sonar.projectName=My Application
sonar.projectVersion=1.0
sonar.sources=src
sonar.tests=tests
sonar.language=ts
sonar.sourceEncoding=UTF-8
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.qualitygate.wait=true

3.3 Semgrep 실전 활용

Semgrep은 가볍고 빠른 SAST 도구로, 커스텀 룰 작성이 쉽습니다.

# .semgrep.yml - 커스텀 보안 룰
rules:
  - id: no-eval-usage
    patterns:
      - pattern: eval(...)
    message: "eval() 사용은 코드 인젝션 위험이 있습니다"
    severity: ERROR
    languages: [javascript, typescript]
    metadata:
      owasp: "A03:2021 Injection"
      cwe: "CWE-95"

  - id: no-innerHTML
    patterns:
      - pattern: $EL.innerHTML = $VAL
    message: "innerHTML 직접 설정은 XSS 위험이 있습니다. textContent 또는 DOMPurify를 사용하세요"
    severity: WARNING
    languages: [javascript, typescript]

  - id: sql-injection-risk
    patterns:
      - pattern: |
          $DB.query(`... ${$VAR} ...`)
      - pattern: |
          $DB.query("..." + $VAR + "...")
    message: "SQL 인젝션 위험! 파라미터 바인딩을 사용하세요"
    severity: ERROR
    languages: [javascript, typescript]
    fix: |
      $DB.query("... $1 ...", [$VAR])

  - id: no-hardcoded-secrets
    patterns:
      - pattern: |
          const $KEY = "AKIA..."
      - pattern: |
          const $KEY = "sk-..."
    message: "하드코딩된 시크릿이 발견되었습니다. 환경 변수를 사용하세요"
    severity: ERROR
    languages: [javascript, typescript]
# Semgrep 실행
semgrep --config auto --config .semgrep.yml src/

# 특정 룰셋만 실행
semgrep --config p/owasp-top-ten src/
semgrep --config p/typescript src/
semgrep --config p/react src/

# CI용 JSON 출력
semgrep --config auto --json --output results.json src/

3.4 CodeQL 고급 쿼리

GitHub CodeQL은 코드를 데이터베이스로 변환하여 쿼리하는 강력한 도구입니다.

// codeql-queries/sql-injection.ql
/**
 * @name SQL injection in query string
 * @description 사용자 입력이 SQL 쿼리에 직접 포함됨
 * @kind path-problem
 * @problem.severity error
 * @security-severity 9.8
 * @precision high
 * @id js/sql-injection
 * @tags security
 *       external/cwe/cwe-089
 */

import javascript
import DataFlow::PathGraph

class SqlInjectionConfig extends TaintTracking::Configuration {
  SqlInjectionConfig() { this = "SqlInjectionConfig" }

  override predicate isSource(DataFlow::Node source) {
    exists(Express::RequestInputAccess input |
      source = input
    )
  }

  override predicate isSink(DataFlow::Node sink) {
    exists(DatabaseAccess query |
      sink = query.getAQueryArgument()
    )
  }
}

from SqlInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
  "이 쿼리는 $@에서 받은 사용자 입력을 포함합니다.",
  source.getNode(), "사용자 입력"

3.5 SAST 도구 비교

기능SonarQubeSemgrepCodeQL
가격Community 무료OSS 무료OSS 무료
지원 언어30+30+10+
커스텀 룰Java DSLYAMLQL Language
CI 통합좋음매우 좋음GitHub 최적화
속도보통매우 빠름느림 (DB 빌드)
정확도높음높음매우 높음
학습 곡선보통낮음높음

4. DAST - 동적 분석

4.1 DAST란?

DAST(Dynamic Application Security Testing)는 실행 중인 애플리케이션에 실제 공격을 시뮬레이션하여 취약점을 탐지합니다. 소스 코드가 필요 없으므로 "블랙박스 테스팅"이라고도 합니다.

4.2 OWASP ZAP 자동화

# zap-automation.yaml
env:
  contexts:
    - name: "My Web App"
      urls:
        - "https://staging.example.com"
      includePaths:
        - "https://staging.example.com/.*"
      excludePaths:
        - "https://staging.example.com/logout"
      authentication:
        method: "form"
        parameters:
          loginUrl: "https://staging.example.com/login"
          loginRequestData: "username=test&password=test123"

jobs:
  - type: spider
    parameters:
      context: "My Web App"
      maxDuration: 5
      maxDepth: 10

  - type: spiderAjax
    parameters:
      context: "My Web App"
      maxDuration: 5

  - type: passiveScan-wait
    parameters:
      maxDuration: 10

  - type: activeScan
    parameters:
      context: "My Web App"
      maxRuleDurationInMins: 5
      policy: "API-Scan"

  - type: report
    parameters:
      template: "modern"
      reportDir: "/zap/reports"
      reportFile: "zap-report"
      reportTitle: "ZAP Security Report"
    risks:
      - high
      - medium
      - low
# Docker로 ZAP 자동화 스캔 실행
docker run --rm -v $(pwd):/zap/wrk:rw \
  -t zaproxy/zap-stable \
  zap.sh -cmd -autorun /zap/wrk/zap-automation.yaml

# API 스캔 (OpenAPI 스펙 기반)
docker run --rm -v $(pwd):/zap/wrk:rw \
  -t zaproxy/zap-stable \
  zap-api-scan.py \
    -t https://staging.example.com/openapi.json \
    -f openapi \
    -r api-report.html

4.3 DAST와 SAST 비교

특성SASTDAST
분석 시점빌드 시런타임
소스 코드 필요아니오
실행 환경 필요아니오
오탐율높음낮음
커버리지전체 코드도달 가능한 경로
취약점 유형코드 레벨런타임/설정
피드백 속도빠름느림

5. SCA - 소프트웨어 구성 분석

5.1 왜 SCA가 중요한가?

현대 애플리케이션의 80% 이상이 오픈소스 코드로 구성됩니다. Log4Shell(CVE-2021-44228)은 단일 오픈소스 라이브러리의 취약점이 전 세계적 영향을 미칠 수 있음을 보여주었습니다.

5.2 Snyk 설정

# .snyk - Snyk 정책 파일
version: v1.5
ignore:
  SNYK-JS-LODASH-590103:
    - '*':
        reason: "해당 함수를 사용하지 않음"
        expires: "2026-06-01T00:00:00.000Z"
patch: {}

language-settings:
  javascript:
    severity-threshold: high
# Snyk CLI 사용법
snyk test                    # 취약점 검사
snyk monitor                 # 지속적 모니터링 등록
snyk test --severity-threshold=high  # 높은 심각도만
snyk code test               # SAST 스캔
snyk container test myimage:latest  # 컨테이너 스캔
snyk iac test terraform/     # IaC 스캔

5.3 Dependabot 설정

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "Asia/Seoul"
    open-pull-requests-limit: 10
    reviewers:
      - "security-team"
    labels:
      - "dependencies"
      - "security"
    commit-message:
      prefix: "deps"
      include: "scope"
    # 보안 업데이트는 자동 머지
    groups:
      security-patches:
        patterns:
          - "*"
        update-types:
          - "security"

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "docker"
      - "dependencies"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "ci"
      - "dependencies"

5.4 Renovate 고급 설정

// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base",
    ":semanticCommits",
    "group:allNonMajor",
    "schedule:weekends"
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"],
    "schedule": ["at any time"]
  },
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "automerge": true,
      "automergeType": "pr",
      "platformAutomerge": true
    },
    {
      "matchPackagePatterns": ["eslint", "prettier"],
      "groupName": "linting tools",
      "automerge": true
    },
    {
      "matchPackagePatterns": ["^@types/"],
      "groupName": "type definitions",
      "automerge": true
    }
  ],
  "prConcurrentLimit": 5,
  "prHourlyLimit": 2
}

6. 컨테이너 보안

6.1 컨테이너 보안의 중요성

컨테이너 이미지에는 OS 패키지, 런타임, 애플리케이션 의존성이 모두 포함됩니다. 하나의 이미지에 수백 개의 취약점이 있을 수 있습니다.

6.2 Trivy 이미지 스캔

# 기본 이미지 스캔
trivy image myapp:latest

# 심각도 필터링
trivy image --severity CRITICAL,HIGH myapp:latest

# 수정 가능한 취약점만
trivy image --ignore-unfixed myapp:latest

# SBOM 생성
trivy image --format cyclonedx --output sbom.json myapp:latest

# CI용 JSON 출력과 종료 코드
trivy image --format json --output results.json \
  --exit-code 1 --severity CRITICAL myapp:latest

# 파일시스템 스캔 (빌드 전)
trivy fs --security-checks vuln,secret,config .

# Kubernetes 매니페스트 스캔
trivy config k8s/

6.3 안전한 Dockerfile 작성

# BAD - 보안 안티패턴
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3
COPY . /app
RUN pip install -r requirements.txt
USER root
EXPOSE 8080
CMD ["python3", "app.py"]
# GOOD - 보안 모범 사례
# 1. 최소 베이스 이미지 사용
FROM python:3.12-slim AS builder

# 2. 패키지 설치 후 캐시 정리
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc && \
    rm -rf /var/lib/apt/lists/*

# 3. 별도 빌드 스테이지
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# 4. 최종 이미지는 최소화
FROM python:3.12-slim

# 5. 비루트 사용자
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 6. 빌드 아티팩트만 복사
COPY --from=builder /install /usr/local
COPY --chown=appuser:appuser . /app

WORKDIR /app
USER appuser

# 7. 읽기 전용 파일시스템 지원
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1

CMD ["python3", "app.py"]

6.4 Grype와 Docker Scout

# Grype - 이미지 취약점 스캔
grype myapp:latest
grype myapp:latest --only-fixed
grype myapp:latest -o json > grype-results.json

# Docker Scout - Docker Desktop 통합
docker scout cves myapp:latest
docker scout recommendations myapp:latest
docker scout quickview myapp:latest

6.5 컨테이너 보안 체크리스트

컨테이너 보안 체크리스트:

이미지 빌드:
  [x] 최소 베이스 이미지 (distroless, alpine, slim)
  [x] 멀티스테이지 빌드
  [x] 비루트 사용자 실행
  [x] 불필요한 패키지 제거
  [x] .dockerignore 설정
  [x] 이미지 서명 (cosign)

런타임:
  [x] 읽기 전용 파일시스템
  [x] 리소스 제한 (CPU, 메모리)
  [x] capabilities 최소화
  [x] seccomp/AppArmor 프로파일
  [x] 네트워크 정책 적용

레지스트리:
  [x] 프라이빗 레지스트리 사용
  [x] 이미지 취약점 스캔
  [x] 태그 불변성 설정
  [x] 이미지 서명 검증

7. Secret 탐지

7.1 유출되는 Secret의 현실

GitHub에 따르면 매년 수백만 건의 시크릿이 공개 리포지토리에 커밋됩니다. API 키, 패스워드, 인증서가 유출되면 몇 분 안에 악용될 수 있습니다.

7.2 GitLeaks 설정

# .gitleaks.toml
title = "Custom Gitleaks Configuration"

[extend]
useDefault = true

[[rules]]
id = "custom-api-key"
description = "Custom API Key Pattern"
regex = '''(?i)api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9]{32,}['"]'''
entropy = 3.5
secretGroup = 0
tags = ["api", "key"]

[[rules]]
id = "private-key-file"
description = "Private Key File Reference"
regex = '''(?i)(private[_-]?key|ssh[_-]?key)\s*[:=]\s*['"].*\.(pem|key|p12)['"]'''
tags = ["key", "private"]

[allowlist]
description = "Global Allowlist"
paths = [
  '''\.gitleaks\.toml''',
  '''test/.*''',
  '''.*_test\.go''',
  '''.*\.test\.(ts|js)'''
]
regexes = [
  '''EXAMPLE_.*''',
  '''REPLACE_ME'''
]
# GitLeaks 실행
gitleaks detect --source . --verbose
gitleaks detect --source . --report-format json --report-path leaks.json

# Git 히스토리 전체 스캔
gitleaks detect --source . --log-opts="--all"

# pre-commit hook
gitleaks protect --staged --verbose

7.3 TruffleHog 활용

# 리포지토리 전체 스캔
trufflehog git file://. --only-verified

# GitHub 조직 전체 스캔
trufflehog github --org=my-org --only-verified

# Docker 이미지 스캔
trufflehog docker --image=myapp:latest

# S3 버킷 스캔
trufflehog s3 --bucket=my-bucket

7.4 pre-commit Hook 설정

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: detect-private-key
      - id: check-added-large-files
        args: ['--maxkb=500']

  - repo: https://github.com/returntocorp/semgrep
    rev: v1.50.0
    hooks:
      - id: semgrep
        args: ['--config', 'p/secrets', '--error']

8. 공급망 보안 - SBOM과 SLSA

8.1 공급망 공격 사례

사건연도영향
SolarWinds202018,000+ 기관 피해
Codecov2021CI 스크립트 변조
ua-parser-js2021npm 패키지 하이재킹
Log4Shell2021전 세계 Java 앱 영향
xz utils2024Linux 백도어 시도

8.2 SBOM 생성

SBOM(Software Bill of Materials)은 소프트웨어에 포함된 모든 구성요소의 목록입니다.

# CycloneDX 포맷 SBOM 생성
# npm 프로젝트
npx @cyclonedx/cyclonedx-npm --output-file sbom.json

# Python 프로젝트
cyclonedx-py requirements requirements.txt -o sbom.json

# 컨테이너 이미지
trivy image --format cyclonedx -o sbom.json myapp:latest
syft myapp:latest -o cyclonedx-json > sbom.json

# SPDX 포맷
syft myapp:latest -o spdx-json > sbom-spdx.json
// SBOM 예시 (CycloneDX 간략)
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "version": 1,
  "metadata": {
    "component": {
      "type": "application",
      "name": "my-web-app",
      "version": "2.1.0"
    }
  },
  "components": [
    {
      "type": "library",
      "name": "express",
      "version": "4.18.2",
      "purl": "pkg:npm/express@4.18.2",
      "licenses": [{ "license": { "id": "MIT" } }]
    },
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21"
    }
  ]
}

8.3 SLSA 프레임워크

SLSA(Supply-chain Levels for Software Artifacts)는 공급망 보안의 성숙도를 측정하는 프레임워크입니다.

SLSA 레벨:
┌──────────────┬─────────────────────────────────────┐
SLSA Level 0 │ 보안 보장 없음                        │
SLSA Level 1 │ 빌드 프로세스 문서화 (provenance)SLSA Level 2 │ 호스팅된 빌드, 서명된 provenance       │
SLSA Level 3 │ 격리된 빌드, 변조 방지                 │
└──────────────┴─────────────────────────────────────┘

8.4 Sigstore로 아티팩트 서명

# cosign으로 컨테이너 이미지 서명
cosign sign --key cosign.key myregistry/myapp:v1.0.0

# 키리스 서명 (OIDC 기반)
cosign sign myregistry/myapp:v1.0.0

# 서명 검증
cosign verify --key cosign.pub myregistry/myapp:v1.0.0

# SBOM 첨부
cosign attach sbom --sbom sbom.json myregistry/myapp:v1.0.0

# 아티팩트 증명 (attestation) 생성
cosign attest --predicate provenance.json \
  --type slsaprovenance \
  myregistry/myapp:v1.0.0

9. Policy-as-Code

9.1 OPA (Open Policy Agent)

# policy/kubernetes.rego
package kubernetes.admission

# 루트 컨테이너 금지
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  container.securityContext.runAsUser == 0
  msg := sprintf("컨테이너 '%s'가 root로 실행됩니다", [container.name])
}

# 최신 태그 금지
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  endswith(container.image, ":latest")
  msg := sprintf("컨테이너 '%s'가 latest 태그를 사용합니다", [container.name])
}

# 리소스 제한 필수
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.resources.limits
  msg := sprintf("컨테이너 '%s'에 리소스 제한이 없습니다", [container.name])
}

# 읽기 전용 루트 파일시스템 필수
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.securityContext.readOnlyRootFilesystem
  msg := sprintf("컨테이너 '%s'가 읽기 전용 파일시스템을 사용하지 않습니다", [container.name])
}

9.2 Kyverno 정책

# kyverno-policies.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-team-label
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "모든 Pod에는 'team' 라벨이 필요합니다"
        pattern:
          metadata:
            labels:
              team: "?*"

---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-privileged-containers
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Privileged 컨테이너는 허용되지 않습니다"
        pattern:
          spec:
            containers:
              - securityContext:
                  privileged: "false"

9.3 Conftest로 IaC 검증

# Conftest로 Terraform 검증
conftest test terraform/ --policy policy/

# Dockerfile 검증
conftest test Dockerfile --policy policy/docker/

# Kubernetes 매니페스트 검증
conftest test k8s/ --policy policy/kubernetes/
# policy/docker/dockerfile.rego
package main

deny[msg] {
  input[i].Cmd == "from"
  val := input[i].Value
  contains(val[0], ":latest")
  msg := "FROM에 latest 태그 사용 금지"
}

deny[msg] {
  input[i].Cmd == "user"
  val := input[i].Value
  val[0] == "root"
  msg := "USER root 사용 금지"
}

10. GitHub Actions 통합 보안 파이프라인

10.1 종합 보안 워크플로우

# .github/workflows/security.yml
name: Security Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # 매주 월요일 오전 6시

permissions:
  contents: read
  security-events: write
  pull-requests: write

jobs:
  # 1단계: Secret 탐지
  secret-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: GitLeaks Scan
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # 2단계: SAST
  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep Scan
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/typescript
            p/react
          generateSarif: "1"

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif
        if: always()

  # 3단계: SCA (의존성 스캔)
  sca:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Snyk Security Scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

  # 4단계: 컨테이너 보안
  container-security:
    runs-on: ubuntu-latest
    needs: [sast]
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker Image
        run: docker build -t myapp:test .

      - name: Trivy Image Scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:test'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

      - name: Upload Trivy SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'
        if: always()

  # 5단계: SBOM 생성
  sbom:
    runs-on: ubuntu-latest
    needs: [container-security]
    steps:
      - uses: actions/checkout@v4

      - name: Generate SBOM
        uses: anchore/sbom-action@v0
        with:
          image: myapp:test
          format: cyclonedx-json
          output-file: sbom.json

      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json

  # 6단계: IaC 스캔
  iac-security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Trivy IaC Scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-iac.sarif'

      - name: Upload IaC SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-iac.sarif'
        if: always()

  # 7단계: DAST (스테이징 배포 후)
  dast:
    runs-on: ubuntu-latest
    needs: [container-security]
    if: github.ref == 'refs/heads/main'
    steps:
      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.10.0
        with:
          target: 'https://staging.example.com'
          rules_file_name: '.zap-rules.tsv'
          fail_action: 'warn'

  # 최종: 보안 게이트
  security-gate:
    runs-on: ubuntu-latest
    needs: [secret-scan, sast, sca, container-security, iac-security]
    steps:
      - name: Security Gate Check
        run: |
          echo "All security checks passed!"
          echo "Pipeline completed at $(date)"

10.2 GitHub Advanced Security 활용

# .github/workflows/codeql.yml
name: CodeQL Analysis

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 4 * * 1'

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    strategy:
      matrix:
        language: ['javascript', 'typescript']
    steps:
      - uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          queries: +security-extended,security-and-quality

      - name: Autobuild
        uses: github/codeql-action/autobuild@v3

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
        with:
          category: "/language:${{ matrix.language }}"

10.3 보안 파이프라인 메트릭

성공적인 DevSecOps 프로그램에서 추적해야 하는 주요 메트릭입니다.

메트릭설명목표
MTTR취약점 평균 수정 시간Critical: 24시간 이내
취약점 밀도코드 1000줄당 취약점 수0.5 이하
스캔 커버리지보안 스캔 적용된 리포지토리 비율100%
오탐율전체 발견 중 오탐 비율20% 이하
파이프라인 통과율보안 게이트 통과 비율85% 이상
의존성 최신도최신 버전 의존성 비율80% 이상

11. 실전 퀴즈

Q1: SAST와 DAST의 차이점은 무엇인가요?

정답:

  • SAST는 소스 코드를 정적으로 분석하여 취약점을 찾습니다. 빌드 단계에서 실행되며 실행 환경이 필요 없습니다.
  • DAST는 실행 중인 애플리케이션에 공격을 시뮬레이션합니다. 런타임 환경이 필요하며 소스 코드 없이도 가능합니다.
  • SAST는 오탐율이 높지만 전체 코드를 분석할 수 있고, DAST는 오탐율이 낮지만 도달 가능한 경로만 분석합니다.
  • 둘 다 사용하는 것이 이상적이며, 이를 IAST(Interactive AST)라고도 합니다.
Q2: Shift-Left 보안이 비용을 절감하는 이유는?

정답:

  • 결함 수정 비용은 발견 시점이 늦을수록 기하급수적으로 증가합니다.
  • 요구사항 단계에서 발견하면 1x, 설계 5x, 구현 10x, 테스트 20x, 운영 30x, 사고 후 100x+ 비용이 듭니다.
  • Shift-Left은 보안을 개발 초기(왼쪽)로 이동시켜 결함을 빨리 발견하므로 비용이 절감됩니다.
  • 또한 개발자가 코드를 작성하는 시점에 피드백을 받으므로 컨텍스트 전환 비용도 줄어듭니다.
Q3: SBOM이 공급망 보안에서 중요한 이유는?

정답:

  • SBOM은 소프트웨어에 포함된 모든 구성요소(라이브러리, 프레임워크 등)의 목록입니다.
  • Log4Shell 같은 취약점이 발견되면 SBOM으로 영향받는 시스템을 즉시 식별할 수 있습니다.
  • 라이선스 컴플라이언스 확인에도 필수적입니다.
  • 미국 행정명령(EO 14028)에서 정부 소프트웨어 공급업체에 SBOM 제출을 의무화했습니다.
  • CycloneDX와 SPDX가 대표적인 SBOM 표준 포맷입니다.
Q4: 안전한 컨테이너 이미지를 만들기 위한 모범 사례 5가지는?

정답:

  1. 최소 베이스 이미지: distroless, alpine, slim 이미지 사용으로 공격 표면 축소
  2. 멀티스테이지 빌드: 빌드 도구를 최종 이미지에 포함시키지 않음
  3. 비루트 사용자: USER 명령으로 루트가 아닌 사용자로 실행
  4. 이미지 서명: cosign으로 이미지 서명 및 검증
  5. 정기적 스캔: Trivy/Grype로 이미지 취약점 정기 스캔

추가 사항: 읽기 전용 파일시스템, capabilities 최소화, 불필요한 패키지 제거

Q5: GitHub Actions에서 보안 파이프라인을 구성할 때 실행 순서는?

정답:

권장 순서:

  1. Secret 탐지 (GitLeaks) - 가장 빠르고 중요
  2. SAST (Semgrep/CodeQL) - 코드 레벨 취약점
  3. SCA (Snyk/Dependabot) - 의존성 취약점
  4. IaC 스캔 (Trivy/Conftest) - 인프라 설정 검증
  5. 컨테이너 보안 (Trivy) - 이미지 빌드 후 스캔
  6. SBOM 생성 - 아티팩트 목록 생성
  7. DAST (ZAP) - 스테이징 배포 후 런타임 테스트
  8. 보안 게이트 - 모든 결과 종합 판단

Secret 탐지와 SAST는 병렬 실행 가능하며, DAST는 배포 후에만 실행됩니다.


12. 참고 자료

  1. OWASP DevSecOps Guideline
  2. NIST Secure Software Development Framework (SSDF)
  3. SLSA - Supply-chain Levels for Software Artifacts
  4. Sigstore - Software Signing
  5. Semgrep Registry
  6. GitHub Security Lab
  7. Trivy Documentation
  8. Snyk Learn - Security Education
  9. OWASP Top 10 (2021)
  10. CycloneDX SBOM Standard
  11. OPA - Open Policy Agent
  12. Kyverno Documentation
  13. CISA SBOM Resources
  14. Google SLSA GitHub Generator

The Complete DevSecOps Guide 2025: Shift-Left Security, SAST/DAST/SCA, Container Security

TL;DR

  • Shift-Left Security: Applying security early in development reduces fix costs by 30x
  • SAST: Detect code-level vulnerabilities with SonarQube, Semgrep, CodeQL
  • DAST: Discover runtime vulnerabilities with OWASP ZAP, Burp Suite
  • SCA: Auto-patch dependency vulnerabilities with Snyk, Dependabot
  • Container Security: Scan images with Trivy, Grype; use minimal base images
  • Supply Chain: Generate SBOMs, achieve SLSA levels, sign with Sigstore
  • Secret Detection: Detect committed secrets with GitLeaks, TruffleHog
  • GitHub Actions: Automate all stages with an integrated security pipeline

Table of Contents

  1. What Is DevSecOps
  2. Shift-Left Security Strategy
  3. SAST - Static Analysis
  4. DAST - Dynamic Analysis
  5. SCA - Software Composition Analysis
  6. Container Security
  7. Secret Detection
  8. Supply Chain Security - SBOM and SLSA
  9. Policy-as-Code
  10. GitHub Actions Integrated Security Pipeline
  11. Practice Quiz
  12. References

1. What Is DevSecOps

1.1 Limitations of Traditional Security

In traditional software development, security was added at the very end. Security teams would review right before deployment. The problems with this approach are clear:

ProblemImpact
Late discoveryFix costs increase 30x
BottleneckSecurity team reviews every project
Developer apathy"Security is the security team's job" mindset
Release delaysEntire schedule delayed when vulnerabilities found
Context gapSecurity team doesn't understand code intent

1.2 Core Principles of DevSecOps

DevSecOps is a portmanteau of Development + Security + Operations. It is both a culture and a practice that integrates security throughout the entire software development lifecycle (SDLC).

Five Core Principles:

  1. Everyone is responsible for security: Security isn't just the security team's job
  2. Automation first: Use automated tools instead of manual reviews
  3. Fast feedback: Security feedback at the point developers write code
  4. Continuous improvement: Measure security metrics and continuously improve
  5. Shared responsibility: Knowledge sharing between dev, ops, and security teams

1.3 DevSecOps Tool Ecosystem Overview

┌─────────────────────────────────────────────────────────────┐
DevSecOps Pipeline├──────────┬──────────┬──────────┬──────────┬────────────────┤
PlanCodeBuildTestDeploy/Run├──────────┼──────────┼──────────┼──────────┼────────────────┤
ThreatSASTSCADASTRuntimeModelingSemgrepSnykZAPProtection│          │ CodeQLTrivyBurpFalcoSecuritySonarQube│ OSVNucleiContainerRequire  │          │          │          │ Security│ ments    │ SecretSBOMFuzzingNetwork│          │ DetectSigning  │          │ Policy├──────────┼──────────┼──────────┼──────────┼────────────────┤
Policy-as-Code (OPA / Kyverno / Conftest)└─────────────────────────────────────────────────────────────┘

2. Shift-Left Security Strategy

2.1 What Is Shift-Left?

Shift-Left is the strategy of moving security activities to the left (earlier stages) of the development process.

Traditional approach:
Plan -> Code -> Build -> Test -> Deploy -> [Security Review] -> Release
                                            ^ Found here = high cost

Shift-Left approach:
Plan -> [Security] -> Code -> [Security] -> Build -> [Security] -> Test -> Deploy
  ^ Start here = low cost

2.2 Shift-Left ROI Analysis

According to research from the IBM Systems Sciences Institute, defect fix costs increase exponentially based on when they are discovered.

Discovery StageRelative CostExample
Requirements1xMissing security requirement found
Design5xArchitecture vulnerability found
Implementation10xCode-level vulnerability found
Testing20xFound during QA
Production30xFound in production
Post-incident100x+Data breach incident

2.3 Implementing Shift-Left

Step 1: Security Champions Program

Designate a security champion in each development team.

# security-champions.yaml
teams:
  - name: "Backend Team"
    champion: "alice@company.com"
    responsibilities:
      - "PR security reviews"
      - "SAST result triage"
      - "Monthly security training"
    training:
      - "OWASP Top 10"
      - "Secure Coding Practices"
      - "Threat Modeling Basics"
  - name: "Frontend Team"
    champion: "bob@company.com"
    responsibilities:
      - "XSS/CSRF prevention review"
      - "Dependency vulnerability management"
      - "CSP header management"

Step 2: Threat Modeling

Perform threat modeling using the STRIDE model during the design phase.

STRIDE Threat Model:
┌─────────────────────────────────────────┐
S - SpoofingT - TamperingR - RepudiationI - Information DisclosureD - Denial of ServiceE - Elevation of Privilege└─────────────────────────────────────────┘

Step 3: IDE Security Plugins

Provide security feedback directly in the developer's IDE.

// .vscode/settings.json
{
  "semgrep.scan.autoScan": true,
  "semgrep.scan.configuration": [
    "p/owasp-top-ten",
    "p/typescript",
    "p/react"
  ],
  "sonarlint.connectedMode.project": {
    "connectionId": "my-sonarqube",
    "projectKey": "my-project"
  }
}

3. SAST - Static Analysis

3.1 What Is SAST?

SAST (Static Application Security Testing) analyzes source code, bytecode, or binaries without execution to find security vulnerabilities.

Key vulnerabilities detected by SAST:

  • SQL Injection
  • Cross-Site Scripting (XSS)
  • Path Traversal
  • Hardcoded secrets
  • Insecure cryptography
  • Buffer overflows

3.2 SonarQube Setup

# docker-compose.yml - SonarQube local setup
version: "3.8"
services:
  sonarqube:
    image: sonarqube:community
    ports:
      - "9000:9000"
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    volumes:
      - sonar_data:/opt/sonarqube/data
      - sonar_logs:/opt/sonarqube/logs
      - sonar_extensions:/opt/sonarqube/extensions
    depends_on:
      - db
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: sonarqube
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  sonar_data:
  sonar_logs:
  sonar_extensions:
  postgres_data:
# sonar-project.properties
sonar.projectKey=my-app
sonar.projectName=My Application
sonar.projectVersion=1.0
sonar.sources=src
sonar.tests=tests
sonar.language=ts
sonar.sourceEncoding=UTF-8
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.qualitygate.wait=true

3.3 Semgrep in Practice

Semgrep is a lightweight and fast SAST tool with easy custom rule authoring.

# .semgrep.yml - Custom security rules
rules:
  - id: no-eval-usage
    patterns:
      - pattern: eval(...)
    message: "eval() usage poses code injection risk"
    severity: ERROR
    languages: [javascript, typescript]
    metadata:
      owasp: "A03:2021 Injection"
      cwe: "CWE-95"

  - id: no-innerHTML
    patterns:
      - pattern: $EL.innerHTML = $VAL
    message: "Direct innerHTML assignment creates XSS risk. Use textContent or DOMPurify"
    severity: WARNING
    languages: [javascript, typescript]

  - id: sql-injection-risk
    patterns:
      - pattern: |
          $DB.query(`... ${$VAR} ...`)
      - pattern: |
          $DB.query("..." + $VAR + "...")
    message: "SQL injection risk! Use parameterized queries"
    severity: ERROR
    languages: [javascript, typescript]
    fix: |
      $DB.query("... $1 ...", [$VAR])

  - id: no-hardcoded-secrets
    patterns:
      - pattern: |
          const $KEY = "AKIA..."
      - pattern: |
          const $KEY = "sk-..."
    message: "Hardcoded secret detected. Use environment variables"
    severity: ERROR
    languages: [javascript, typescript]
# Run Semgrep
semgrep --config auto --config .semgrep.yml src/

# Run specific rulesets only
semgrep --config p/owasp-top-ten src/
semgrep --config p/typescript src/
semgrep --config p/react src/

# JSON output for CI
semgrep --config auto --json --output results.json src/

3.4 Advanced CodeQL Queries

GitHub CodeQL is a powerful tool that converts code into a database for querying.

// codeql-queries/sql-injection.ql
/**
 * @name SQL injection in query string
 * @description User input included directly in SQL query
 * @kind path-problem
 * @problem.severity error
 * @security-severity 9.8
 * @precision high
 * @id js/sql-injection
 * @tags security
 *       external/cwe/cwe-089
 */

import javascript
import DataFlow::PathGraph

class SqlInjectionConfig extends TaintTracking::Configuration {
  SqlInjectionConfig() { this = "SqlInjectionConfig" }

  override predicate isSource(DataFlow::Node source) {
    exists(Express::RequestInputAccess input |
      source = input
    )
  }

  override predicate isSink(DataFlow::Node sink) {
    exists(DatabaseAccess query |
      sink = query.getAQueryArgument()
    )
  }
}

from SqlInjectionConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
  "This query includes user input from $@.",
  source.getNode(), "user input"

3.5 SAST Tool Comparison

FeatureSonarQubeSemgrepCodeQL
PriceCommunity freeOSS freeOSS free
Languages30+30+10+
Custom rulesJava DSLYAMLQL Language
CI integrationGoodVery goodGitHub optimized
SpeedMediumVery fastSlow (DB build)
AccuracyHighHighVery high
Learning curveMediumLowHigh

4. DAST - Dynamic Analysis

4.1 What Is DAST?

DAST (Dynamic Application Security Testing) detects vulnerabilities by simulating real attacks against a running application. Since it doesn't require source code, it's also called "black-box testing."

4.2 OWASP ZAP Automation

# zap-automation.yaml
env:
  contexts:
    - name: "My Web App"
      urls:
        - "https://staging.example.com"
      includePaths:
        - "https://staging.example.com/.*"
      excludePaths:
        - "https://staging.example.com/logout"
      authentication:
        method: "form"
        parameters:
          loginUrl: "https://staging.example.com/login"
          loginRequestData: "username=test&password=test123"

jobs:
  - type: spider
    parameters:
      context: "My Web App"
      maxDuration: 5
      maxDepth: 10

  - type: spiderAjax
    parameters:
      context: "My Web App"
      maxDuration: 5

  - type: passiveScan-wait
    parameters:
      maxDuration: 10

  - type: activeScan
    parameters:
      context: "My Web App"
      maxRuleDurationInMins: 5
      policy: "API-Scan"

  - type: report
    parameters:
      template: "modern"
      reportDir: "/zap/reports"
      reportFile: "zap-report"
      reportTitle: "ZAP Security Report"
    risks:
      - high
      - medium
      - low
# Run ZAP automated scan with Docker
docker run --rm -v $(pwd):/zap/wrk:rw \
  -t zaproxy/zap-stable \
  zap.sh -cmd -autorun /zap/wrk/zap-automation.yaml

# API scan (OpenAPI spec based)
docker run --rm -v $(pwd):/zap/wrk:rw \
  -t zaproxy/zap-stable \
  zap-api-scan.py \
    -t https://staging.example.com/openapi.json \
    -f openapi \
    -r api-report.html

4.3 DAST vs SAST Comparison

CharacteristicSASTDAST
Analysis timingBuild timeRuntime
Source code requiredYesNo
Execution environment neededNoYes
False positive rateHighLow
CoverageEntire codebaseReachable paths
Vulnerability typesCode levelRuntime/config
Feedback speedFastSlow

5. SCA - Software Composition Analysis

5.1 Why SCA Matters

Over 80% of modern applications consist of open-source code. Log4Shell (CVE-2021-44228) demonstrated that a single open-source library vulnerability can have worldwide impact.

5.2 Snyk Configuration

# .snyk - Snyk policy file
version: v1.5
ignore:
  SNYK-JS-LODASH-590103:
    - '*':
        reason: "We don't use the affected function"
        expires: "2026-06-01T00:00:00.000Z"
patch: {}

language-settings:
  javascript:
    severity-threshold: high
# Snyk CLI usage
snyk test                    # Vulnerability check
snyk monitor                 # Register for continuous monitoring
snyk test --severity-threshold=high  # High severity only
snyk code test               # SAST scan
snyk container test myimage:latest  # Container scan
snyk iac test terraform/     # IaC scan

5.3 Dependabot Configuration

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "America/New_York"
    open-pull-requests-limit: 10
    reviewers:
      - "security-team"
    labels:
      - "dependencies"
      - "security"
    commit-message:
      prefix: "deps"
      include: "scope"
    groups:
      security-patches:
        patterns:
          - "*"
        update-types:
          - "security"

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "docker"
      - "dependencies"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "ci"
      - "dependencies"

5.4 Advanced Renovate Configuration

// renovate.json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base",
    ":semanticCommits",
    "group:allNonMajor",
    "schedule:weekends"
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"],
    "schedule": ["at any time"]
  },
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "automerge": true,
      "automergeType": "pr",
      "platformAutomerge": true
    },
    {
      "matchPackagePatterns": ["eslint", "prettier"],
      "groupName": "linting tools",
      "automerge": true
    },
    {
      "matchPackagePatterns": ["^@types/"],
      "groupName": "type definitions",
      "automerge": true
    }
  ],
  "prConcurrentLimit": 5,
  "prHourlyLimit": 2
}

6. Container Security

6.1 Why Container Security Matters

Container images bundle OS packages, runtimes, and application dependencies. A single image can contain hundreds of vulnerabilities.

6.2 Trivy Image Scanning

# Basic image scan
trivy image myapp:latest

# Filter by severity
trivy image --severity CRITICAL,HIGH myapp:latest

# Only fixable vulnerabilities
trivy image --ignore-unfixed myapp:latest

# Generate SBOM
trivy image --format cyclonedx --output sbom.json myapp:latest

# JSON output with exit code for CI
trivy image --format json --output results.json \
  --exit-code 1 --severity CRITICAL myapp:latest

# Filesystem scan (before build)
trivy fs --security-checks vuln,secret,config .

# Kubernetes manifest scan
trivy config k8s/

6.3 Writing Secure Dockerfiles

# BAD - Security anti-patterns
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3
COPY . /app
RUN pip install -r requirements.txt
USER root
EXPOSE 8080
CMD ["python3", "app.py"]
# GOOD - Security best practices
# 1. Use minimal base image
FROM python:3.12-slim AS builder

# 2. Clean cache after package install
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc && \
    rm -rf /var/lib/apt/lists/*

# 3. Separate build stage
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# 4. Minimize final image
FROM python:3.12-slim

# 5. Non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 6. Copy only build artifacts
COPY --from=builder /install /usr/local
COPY --chown=appuser:appuser . /app

WORKDIR /app
USER appuser

# 7. Support read-only filesystem
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1

CMD ["python3", "app.py"]

6.4 Grype and Docker Scout

# Grype - Image vulnerability scanning
grype myapp:latest
grype myapp:latest --only-fixed
grype myapp:latest -o json > grype-results.json

# Docker Scout - Docker Desktop integration
docker scout cves myapp:latest
docker scout recommendations myapp:latest
docker scout quickview myapp:latest

6.5 Container Security Checklist

Container Security Checklist:

Image Build:
  [x] Minimal base image (distroless, alpine, slim)
  [x] Multi-stage build
  [x] Non-root user execution
  [x] Remove unnecessary packages
  [x] .dockerignore configured
  [x] Image signing (cosign)

Runtime:
  [x] Read-only filesystem
  [x] Resource limits (CPU, memory)
  [x] Minimize capabilities
  [x] seccomp/AppArmor profiles
  [x] Network policies applied

Registry:
  [x] Use private registry
  [x] Image vulnerability scanning
  [x] Tag immutability
  [x] Image signature verification

7. Secret Detection

7.1 The Reality of Leaked Secrets

According to GitHub, millions of secrets are committed to public repositories every year. Leaked API keys, passwords, and certificates can be exploited within minutes.

7.2 GitLeaks Configuration

# .gitleaks.toml
title = "Custom Gitleaks Configuration"

[extend]
useDefault = true

[[rules]]
id = "custom-api-key"
description = "Custom API Key Pattern"
regex = '''(?i)api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9]{32,}['"]'''
entropy = 3.5
secretGroup = 0
tags = ["api", "key"]

[[rules]]
id = "private-key-file"
description = "Private Key File Reference"
regex = '''(?i)(private[_-]?key|ssh[_-]?key)\s*[:=]\s*['"].*\.(pem|key|p12)['"]'''
tags = ["key", "private"]

[allowlist]
description = "Global Allowlist"
paths = [
  '''\.gitleaks\.toml''',
  '''test/.*''',
  '''.*_test\.go''',
  '''.*\.test\.(ts|js)'''
]
regexes = [
  '''EXAMPLE_.*''',
  '''REPLACE_ME'''
]
# Run GitLeaks
gitleaks detect --source . --verbose
gitleaks detect --source . --report-format json --report-path leaks.json

# Scan entire Git history
gitleaks detect --source . --log-opts="--all"

# pre-commit hook
gitleaks protect --staged --verbose

7.3 TruffleHog Usage

# Full repository scan
trufflehog git file://. --only-verified

# Scan entire GitHub organization
trufflehog github --org=my-org --only-verified

# Docker image scan
trufflehog docker --image=myapp:latest

# S3 bucket scan
trufflehog s3 --bucket=my-bucket

7.4 Pre-commit Hook Setup

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: detect-private-key
      - id: check-added-large-files
        args: ['--maxkb=500']

  - repo: https://github.com/returntocorp/semgrep
    rev: v1.50.0
    hooks:
      - id: semgrep
        args: ['--config', 'p/secrets', '--error']

8. Supply Chain Security - SBOM and SLSA

8.1 Supply Chain Attack Case Studies

IncidentYearImpact
SolarWinds202018,000+ organizations affected
Codecov2021CI script tampering
ua-parser-js2021npm package hijacking
Log4Shell2021Global Java app impact
xz utils2024Linux backdoor attempt

8.2 Generating SBOMs

An SBOM (Software Bill of Materials) is a list of all components included in software.

# Generate CycloneDX format SBOM
# npm project
npx @cyclonedx/cyclonedx-npm --output-file sbom.json

# Python project
cyclonedx-py requirements requirements.txt -o sbom.json

# Container image
trivy image --format cyclonedx -o sbom.json myapp:latest
syft myapp:latest -o cyclonedx-json > sbom.json

# SPDX format
syft myapp:latest -o spdx-json > sbom-spdx.json
// SBOM example (CycloneDX abbreviated)
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "version": 1,
  "metadata": {
    "component": {
      "type": "application",
      "name": "my-web-app",
      "version": "2.1.0"
    }
  },
  "components": [
    {
      "type": "library",
      "name": "express",
      "version": "4.18.2",
      "purl": "pkg:npm/express@4.18.2",
      "licenses": [{ "license": { "id": "MIT" } }]
    },
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21"
    }
  ]
}

8.3 SLSA Framework

SLSA (Supply-chain Levels for Software Artifacts) is a framework for measuring supply chain security maturity.

SLSA Levels:
┌──────────────┬─────────────────────────────────────┐
SLSA Level 0No security guarantees              │
SLSA Level 1Build process documented (provenance)SLSA Level 2Hosted build, signed provenance      │
SLSA Level 3Isolated build, tamper-resistant      │
└──────────────┴─────────────────────────────────────┘

8.4 Signing Artifacts with Sigstore

# Sign container image with cosign
cosign sign --key cosign.key myregistry/myapp:v1.0.0

# Keyless signing (OIDC-based)
cosign sign myregistry/myapp:v1.0.0

# Verify signature
cosign verify --key cosign.pub myregistry/myapp:v1.0.0

# Attach SBOM
cosign attach sbom --sbom sbom.json myregistry/myapp:v1.0.0

# Create artifact attestation
cosign attest --predicate provenance.json \
  --type slsaprovenance \
  myregistry/myapp:v1.0.0

9. Policy-as-Code

9.1 OPA (Open Policy Agent)

# policy/kubernetes.rego
package kubernetes.admission

# Deny root containers
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  container.securityContext.runAsUser == 0
  msg := sprintf("Container '%s' runs as root", [container.name])
}

# Deny latest tag
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  endswith(container.image, ":latest")
  msg := sprintf("Container '%s' uses latest tag", [container.name])
}

# Require resource limits
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.resources.limits
  msg := sprintf("Container '%s' has no resource limits", [container.name])
}

# Require read-only root filesystem
deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.securityContext.readOnlyRootFilesystem
  msg := sprintf("Container '%s' doesn't use read-only filesystem", [container.name])
}

9.2 Kyverno Policies

# kyverno-policies.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-team-label
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "All Pods must have a 'team' label"
        pattern:
          metadata:
            labels:
              team: "?*"

---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-privileged-containers
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Privileged containers are not allowed"
        pattern:
          spec:
            containers:
              - securityContext:
                  privileged: "false"

9.3 IaC Validation with Conftest

# Validate Terraform with Conftest
conftest test terraform/ --policy policy/

# Validate Dockerfile
conftest test Dockerfile --policy policy/docker/

# Validate Kubernetes manifests
conftest test k8s/ --policy policy/kubernetes/
# policy/docker/dockerfile.rego
package main

deny[msg] {
  input[i].Cmd == "from"
  val := input[i].Value
  contains(val[0], ":latest")
  msg := "Using latest tag in FROM is prohibited"
}

deny[msg] {
  input[i].Cmd == "user"
  val := input[i].Value
  val[0] == "root"
  msg := "USER root is prohibited"
}

10. GitHub Actions Integrated Security Pipeline

10.1 Comprehensive Security Workflow

# .github/workflows/security.yml
name: Security Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # Every Monday at 6 AM

permissions:
  contents: read
  security-events: write
  pull-requests: write

jobs:
  # Stage 1: Secret Detection
  secret-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: GitLeaks Scan
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # Stage 2: SAST
  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep Scan
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/typescript
            p/react
          generateSarif: "1"

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif
        if: always()

  # Stage 3: SCA (Dependency Scan)
  sca:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Snyk Security Scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

  # Stage 4: Container Security
  container-security:
    runs-on: ubuntu-latest
    needs: [sast]
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker Image
        run: docker build -t myapp:test .

      - name: Trivy Image Scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:test'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
          exit-code: '1'

      - name: Upload Trivy SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'
        if: always()

  # Stage 5: SBOM Generation
  sbom:
    runs-on: ubuntu-latest
    needs: [container-security]
    steps:
      - uses: actions/checkout@v4

      - name: Generate SBOM
        uses: anchore/sbom-action@v0
        with:
          image: myapp:test
          format: cyclonedx-json
          output-file: sbom.json

      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json

  # Stage 6: IaC Scan
  iac-security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Trivy IaC Scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-iac.sarif'

      - name: Upload IaC SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-iac.sarif'
        if: always()

  # Stage 7: DAST (after staging deployment)
  dast:
    runs-on: ubuntu-latest
    needs: [container-security]
    if: github.ref == 'refs/heads/main'
    steps:
      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.10.0
        with:
          target: 'https://staging.example.com'
          rules_file_name: '.zap-rules.tsv'
          fail_action: 'warn'

  # Final: Security Gate
  security-gate:
    runs-on: ubuntu-latest
    needs: [secret-scan, sast, sca, container-security, iac-security]
    steps:
      - name: Security Gate Check
        run: |
          echo "All security checks passed!"
          echo "Pipeline completed at $(date)"

10.2 GitHub Advanced Security

# .github/workflows/codeql.yml
name: CodeQL Analysis

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 4 * * 1'

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    strategy:
      matrix:
        language: ['javascript', 'typescript']
    steps:
      - uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          queries: +security-extended,security-and-quality

      - name: Autobuild
        uses: github/codeql-action/autobuild@v3

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3
        with:
          category: "/language:${{ matrix.language }}"

10.3 Security Pipeline Metrics

Key metrics to track in a successful DevSecOps program:

MetricDescriptionTarget
MTTRMean time to remediateCritical: within 24 hours
Vulnerability densityVulnerabilities per 1000 LOCBelow 0.5
Scan coverageRepos with security scanning100%
False positive rateFalse positives among all findingsBelow 20%
Pipeline pass rateSecurity gate pass rateAbove 85%
Dependency freshnessDependencies on latest versionAbove 80%

11. Practice Quiz

Q1: What is the difference between SAST and DAST?

Answer:

  • SAST statically analyzes source code to find vulnerabilities. It runs during the build phase and doesn't require an execution environment.
  • DAST simulates attacks against a running application. It requires a runtime environment and can work without source code.
  • SAST has a higher false positive rate but can analyze the entire codebase, while DAST has a lower false positive rate but only analyzes reachable paths.
  • Using both is ideal, and this combination is sometimes called IAST (Interactive AST).
Q2: Why does Shift-Left security reduce costs?

Answer:

  • Defect fix costs increase exponentially the later they are discovered.
  • Discovery at requirements costs 1x, design 5x, implementation 10x, testing 20x, production 30x, post-incident 100x+.
  • Shift-Left moves security to the early (left) stages of development, finding defects sooner and reducing costs.
  • Additionally, developers receive feedback while writing code, reducing context-switching costs.
Q3: Why is SBOM important for supply chain security?

Answer:

  • An SBOM is a list of all components (libraries, frameworks, etc.) included in software.
  • When vulnerabilities like Log4Shell are discovered, SBOMs enable immediate identification of affected systems.
  • SBOMs are also essential for license compliance verification.
  • US Executive Order 14028 mandated SBOM submission for government software suppliers.
  • CycloneDX and SPDX are the two primary SBOM standard formats.
Q4: What are 5 best practices for creating secure container images?

Answer:

  1. Minimal base images: Use distroless, alpine, or slim images to reduce attack surface
  2. Multi-stage builds: Don't include build tools in the final image
  3. Non-root user: Use the USER directive to run as a non-root user
  4. Image signing: Sign and verify images with cosign
  5. Regular scanning: Regularly scan images with Trivy/Grype

Additional: read-only filesystem, minimize capabilities, remove unnecessary packages

Q5: What is the recommended execution order for a security pipeline in GitHub Actions?

Answer:

Recommended order:

  1. Secret detection (GitLeaks) - fastest and most critical
  2. SAST (Semgrep/CodeQL) - code-level vulnerabilities
  3. SCA (Snyk/Dependabot) - dependency vulnerabilities
  4. IaC scanning (Trivy/Conftest) - infrastructure config validation
  5. Container security (Trivy) - scan after image build
  6. SBOM generation - create artifact inventory
  7. DAST (ZAP) - runtime testing after staging deployment
  8. Security gate - aggregate all results

Secret detection and SAST can run in parallel. DAST can only run after deployment.


12. References

  1. OWASP DevSecOps Guideline
  2. NIST Secure Software Development Framework (SSDF)
  3. SLSA - Supply-chain Levels for Software Artifacts
  4. Sigstore - Software Signing
  5. Semgrep Registry
  6. GitHub Security Lab
  7. Trivy Documentation
  8. Snyk Learn - Security Education
  9. OWASP Top 10 (2021)
  10. CycloneDX SBOM Standard
  11. OPA - Open Policy Agent
  12. Kyverno Documentation
  13. CISA SBOM Resources
  14. Google SLSA GitHub Generator