kmathanprasath/DevSecOps-Best-Practices
GitHub: kmathanprasath/DevSecOps-Best-Practices
一份从零到生产级的DevSecOps实践指南,系统讲解如何在软件交付全生命周期中嵌入安全扫描与防护,涵盖Git加固、IaC审计、容器安全、K8s加固及完整CI/CD安全流水线构建。
Stars: 1 | Forks: 0
# 🔐 DevSecOps:从零到生产级安全
### DevOps + 安全思维 = DevSecOps
[](LICENSE)
[](CONTRIBUTING.md)
[](#shift-left)
## 📖 这份指南是什么?
这是一份**完整、从入门到精通的 DevSecOps 参考**,旨在让即使是从未接触过终端的人也能理解安全在软件交付生命周期的每一步中*为何*重要。
无论你是开发者、QA 工程师、云工程师,还是完全的新手,阅读本指南后你都会有所收获,因为我在这里分享了我的 DevSecOps 全面经验:
- 什么是 DevSecOps 以及它为何存在
- 如何保护 Git、Infrastructure as Code、容器和 Kubernetes
- 如何构建能自动捕获漏洞的 CI/CD 流水线
- SAST、DAST 和 SCA 代表什么以及如何使用它们
## 📚 目录
1. [全局概览:什么是 DevSecOps?](#1-the-big-picture--what-is-devsecops)
2. [左移与右防](#2-shift-left-vs-shield-right)
3. [威胁建模](#3-threat-modelling)
4. [Git 的 DevSecOps](#4-devsecops-for-git)
5. [IaC 安全 Terraform](#5-iac-security--terraform)
6. [容器安全 Docker](#6-container-security--docker)
7. [Kubernetes 安全](#7-kubernetes-security)
8. [CI/CD 流水线安全](#8-cicd-pipeline-security)
9. [应用安全 SAST, DAST, SCA](#9-application-security--sast-dast-sca)
10. [终极安全流水线](#10-the-ultimate-secure-pipeline)
## 1. 全局概览:什么是 DevSecOps?
### 旧世界(没有安全的 DevOps)
想象一家制造汽车的工厂。这家工厂速度很快,每小时都有汽车下线。但是直到汽车售出并上路之前,都没有人检查刹车。当刹车失灵时,灾难就发生了。
这正是软件中将安全视为事后诸葛亮时会发生的情况。
```
Developer writes code → Code is tested → Code is deployed → Security team scans → Vulnerabilities found → Fix → Redeploy
↑
(Too late. Already in production.)
```
### DevSecOps 世界
DevSecOps 在**每一个阶段**都嵌入了安全检查,从开发者敲下第一行代码的那一刻就开始了。
```
Developer writes code
↓
Pre-commit hook blocks secrets ← Security at commit time
↓
Pull Request triggers CI pipeline
↓
SAST scans source code ← Security at build time
SCA checks dependencies
Container image scanned with Trivy
↓
Staging deployment
↓
DAST attacks the running app ← Security at runtime
↓
Production deployment
↓
Runtime monitoring + alerts ← Security in production
```
### DevSecOps 的范围
| 领域 | DevSecOps 工程师负责的安全内容 |
|------|----------------------------------|
| **Git / VCS** | 代码中无密钥泄露、分支保护、RBAC |
| **IaC (Terraform)** | 无硬编码凭证、无配置错误的基础设施 |
| **脚本编写 (Python/Bash)** | 无易受攻击的包、无硬编码密码 |
| **容器 (Docker)** | 非 root 用户、最小化镜像、无 CVE |
| **Kubernetes** | 私有集群、RBAC、网络策略、密钥管理 |
| **CI/CD** | 每条流水线中进行自动化的 SAST、DAST、SCA 和镜像扫描 |
### 为什么 AI 让 DevSecOps 变得更重要,而不是无关紧要
如今,在许多组织中,AI 编写了大约 **80% 的代码**。开发者使用 GitHub Copilot、ChatGPT 和类似的工具。这对提高速度很有好处,但 AI 会:
- 复制互联网上的模式,包括 **Stack Overflow 上的易受攻击代码**
- 使用带有已知 CVE 的 **过时包**
- 不了解你所在组织的特定安全要求
DevSecOps 工程师就是那张能网住 AI 遗漏之处的安全网。
## 2. 左移与右防
这是 DevSecOps 中**最基本的概念**。在做任何其他事情之前,你必须理解这一点。
### 软件交付的时间线
```
LEFT RIGHT
| |
▼ ▼
Dev writes code → Build → Test → Stage → Deploy → Production → Monitor
```
- **左侧** = 应用开发开始的地方(开发者的笔记本电脑)
- **右侧** = 应用在生产环境中运行的地方(用户正在使用它)
### 右防(旧方法)
右防意味着你只在**右侧**添加安全措施——即应用程序已经部署之后。
右防工具的例子:
- 带有速率限制的 API Gateway
- 应用前端的 WAF (Web Application Firewall)
- EKS 集群上的运行时安全监控
**问题在于:** 当你在右侧发现漏洞时,它通常已经在你代码库中存在数周或数月了。修复它不仅昂贵、缓慢,而且充满风险。
### 左移(DevSecOps 方法)
左移意味着你将安全检查尽可能地移到**左侧**——理想情况下是在代码提交之前的开发者机器上。
```
🔒 Pre-commit hooks → catches secrets before they leave the laptop
🔒 SAST in CI pipeline → catches code vulnerabilities at build time
🔒 SCA in CI pipeline → catches vulnerable dependencies at build time
🔒 Container scanning → catches CVEs before the image is deployed
🔒 IaC scanning (Checkov) → catches misconfigurations before infra is created
🔒 DAST in staging → catches runtime vulnerabilities before production
```
### 组合方法(最佳实践)
你不必只选择其中一个。你要**两者兼顾**:
| 阶段 | 左移 | 右防 |
|-------|-----------|--------------|
| 编码 | Pre-commit 钩子、SAST | — |
| 构建 | SCA、容器扫描 | — |
| 部署 | IaC 扫描、Kyverno 策略 | API Gateway、WAF |
| 运行时 | — | 网络策略、监控、告警 |
## 3. 威胁建模
在编写任何一行代码之前,DevSecOps 工程师会问:**“可能会出什么问题?”**
这就是威胁建模——一种在安全风险**变成实际问题之前**对其进行识别、分析和缓解的结构化方法。
### 三步流程
```
Step 1: IDENTIFY Step 2: ANALYZE Step 3: MITIGATE
───────────────── ───────────────── ─────────────────
What assets do How likely is What controls can
we have? this attack? we put in place?
What can go wrong? How severe? How do we test them?
```
### STRIDE 框架
STRIDE 是用于分类威胁的行业标准框架:
| 字母 | 威胁 | 通俗解释 | 示例 |
|--------|--------|---------------|---------|
| **S** | Spoofing (欺骗) | 假冒他人身份 | 攻击者以管理员身份登录 |
| **T** | Tampering (篡改) | 未经允许修改数据 | 攻击者将订单金额改为 $0 |
| **R** | Repudiation (抵赖) | 否认做过某事 | 开发者否认删除了数据库 |
| **I** | Information Disclosure (信息泄露) | 泄露敏感数据 | API 在响应中返回密码 |
| **D** | Denial of Service (拒绝服务) | 使系统无法使用 | 用大量请求淹没服务器 |
| **E** | Elevation of Privilege (权限提升) | 获得超出允许范围的访问权限 | 普通用户访问了管理员面板 |
### 工具:OWASP Threat Dragon
OWASP Threat Dragon 是一个免费的开源工具,可让你**绘制图表**来展示你的应用程序,并自动识别威胁。
```
How it works:
1. Draw your system (web app → API → database → external services)
2. Mark trust boundaries (where data crosses security zones)
3. Threat Dragon automatically suggests STRIDE threats for each component
4. You document mitigations for each threat
```
### 真实示例:三层 Web 应用
```
[Browser] HTTPS──► [Load Balancer] ──► [App Server] ──► [Database]
| |
Trust Boundary Trust Boundary
Threats identified:
- Browser → LB: Spoofing (fake requests), DoS (flood)
- LB → App: Tampering (modified headers)
- App → DB: SQL Injection, Information Disclosure
- App Server: Elevation of Privilege (running as root)
```
## 4. Git 的 DevSecOps
Git 是一切开始的地方。开发者的第一个动作就是将代码推送到代码库。如果秘密信息在这里泄露,**下游的一切都会受到威胁**。
### 为什么 Git 安全很重要
这并非假设。这种情况每周都在发生。
### 4.1 `.gitignore` 第一道防线
`.gitignore` 告诉 Git:**“不要跟踪这些文件。”**
如果没有它,开发者可能会意外提交:
- `.env` 文件(数据库密码、API 密钥)
- `terraform.tfstate`(包含基础设施秘密信息)
- `.pem` 文件(SSH 私钥)
- `node_modules/`(数千个会使代码库臃肿的文件)
**在每个项目的根目录下创建一个 `.gitignore`:**
```
# 环境文件 绝对不要提交这些
.env
.env.local
.env.production
.env.*
# Terraform state 包含真实的基础设施 secrets
*.tfstate
*.tfstate.backup
.terraform/
# SSH 和证书
*.pem
*.key
*.p12
*.pfx
# 云凭证
.aws/credentials
.gcp/
credentials.json
# IDE 和 OS 文件
.DS_Store
.vscode/
*.swp
# 依赖目录
node_modules/
__pycache__/
.venv/
vendor/
```
### 4.2 Pre-Commit 钩子:在机密离开你的电脑前拦截它
`.gitignore` 可防止意外提交文件。但如果开发者直接在 `app.py` 中**硬编码**了密码怎么办?
```
# 错误:源代码中硬编码的 secret
DB_PASSWORD = "SuperSecret123!"
```
`.gitignore` 无法捕获这种情况。这就是 **pre-commit 钩子**发挥作用的地方。
#### Pre-Commit 钩子如何工作
```
Developer runs: git commit -m "add feature"
↓
Pre-commit hook fires BEFORE the commit is saved
↓
Hook scans all staged files for patterns
↓
Found secret? → BLOCK commit, show error
No secret? → Allow commit to proceed
```
#### 选项 A:原生 Git 钩子(自定义脚本)
`.git/hooks/pre-commit` 文件会在每次提交前自动运行。
```
# .git/hooks/pre-commit
#!/bin/bash
echo "🔍 Scanning for secrets..."
# 检查常见的 secret 模式
PATTERNS=(
"password\s*=\s*['\"][^'\"]{6,}"
"secret\s*=\s*['\"][^'\"]{6,}"
"api_key\s*=\s*['\"][^'\"]{6,}"
"AKIA[0-9A-Z]{16}" # AWS Access Key
"-----BEGIN.*PRIVATE KEY-----"
)
for pattern in "${PATTERNS[@]}"; do
if git diff --cached | grep -iE "$pattern"; then
echo "❌ SECRET DETECTED! Commit blocked."
echo " Remove the secret and use environment variables instead."
exit 1
fi
done
echo "✅ No secrets found. Proceeding with commit."
exit 0
```
```
# 使其可执行
chmod +x .git/hooks/pre-commit
```
**局限性:** 你必须知道每一种可能的秘密模式。你无法用自定义脚本覆盖所有情况。
#### 选项 B:带有 Gitleaks 的 `pre-commit` 框架(推荐)
`pre-commit` 框架是一个开源工具,可为你管理各种钩子。Gitleaks 是一个了解**数千种秘密模式**的工具,包括 AWS 密钥、GitHub token、Stripe 密钥、Slack webhook 等等。
**步骤 1:安装 pre-commit 框架**
```
pip install pre-commit
```
**步骤 2:在代码库的根目录创建 `.pre-commit-config.yaml`**
```
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaks
name: Detect hardcoded secrets
description: Detect secrets using Gitleaks
entry: gitleaks protect --staged --redact --config .gitleaks.toml
language: golang
pass_filenames: false
# Bonus: also check for large files accidentally staged
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
args: ['--maxkb=500']
- id: detect-private-key
- id: check-merge-conflict
```
**步骤 3:将钩子安装到你的代码库中**
```
pre-commit install
```
**步骤 4:自动保持钩子更新**
```
pre-commit autoupdate
```
现在,每次执行 `git commit` 都会自动运行 Gitleaks。如果发现秘密信息,提交将被阻止,并显示清晰的错误消息。
### 4.3 Gitleaks 扫描整个代码库历史
Pre-commit 钩子保护的是未来的提交。但是,**2 年前提交的秘密信息**怎么办?
获得代码库访问权限的攻击者总是会首先检查完整的提交历史。
```
# 扫描整个 repo 历史记录以查找 secrets
gitleaks detect --source . --verbose
# 其工作原理:
# - 遍历 repo 开始以来的每一个 commit
# - 使用 150 多个 secret 模式检查每个文件更改
# - 报告:哪个文件、哪个 commit、哪一行、什么类型的 secret
```
**示例输出:**
```
Finding: password = "SuperSecret123!"
Secret: SuperSecret123!
RuleID: generic-password
Entropy: 3.58
File: config/database.py
Line: 14
Commit: a3f9c2d
Author: john.doe@company.com
Date: 2023-06-15
```
如果在历史记录中发现了秘密信息,你必须:
1. 立即轮换该秘密(假设它已泄露)
2. 使用 `git filter-branch` 或 BFG Repo Cleaner 将其从历史记录中删除
3. 强制推送清理后的历史记录
### 4.4 GitHub Actions 中的 Gitleaks:在每次 PR 上自动扫描
你无法强迫每个开发者都安装 pre-commit 钩子。但你**可以**在 CI 中强制执行扫描。
```
# .github/workflows/gitleaks.yaml
name: Secret Scanning
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]
workflow_dispatch: # allows manual trigger from GitHub UI
jobs:
gitleaks-scan:
name: Scan for Secrets
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # IMPORTANT: fetch full history, not just latest commit
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Only needed for GitHub Organizations (not personal accounts):
# GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
```
### 4.5 分支保护规则:强制执行代码审查
默认情况下,任何具有写入权限的人都可以直接推送到 `main` 分支。这非常危险。
**最佳实践:** 任何人——甚至代码库所有者——都不应直接推送到 `main`。所有更改都必须通过 Pull Request 进行。
**如何在 GitHub 中设置:**
```
GitHub Repo → Settings → Branches → Add branch ruleset
Name: protect-main
Enforcement: Active
Target branches: main, releases-*
Rules to enable:
✅ Require a pull request before merging
└─ Required approvals: 2
└─ Require review from Code Owners
└─ Dismiss stale reviews when new commits are pushed
✅ Require status checks to pass before merging
└─ Add: gitleaks-scan (your CI job name)
└─ Add: any other required CI checks
✅ Require branches to be up to date before merging
✅ Block force pushes
✅ Restrict deletions
```
这意味着只有在满足以下条件时才能合并 PR:
1. 所有 CI 检查均通过(包括 Gitleaks)
2. 至少有 2 名评审者批准
3. Code Owner 已审查(如果适用)
### 4.6 CODEOWNERS 强制专家审查
在大型项目中,你希望特定的人员审查代码库的特定部分。
```
# .github/CODEOWNERS
# 全局规则 安全团队审查所有内容
* @security-team
# Terraform 更改必须由 cloud 团队审查
/terraform/ @cloud-team
# 支付服务 仅高级工程师
/services/payments/ @senior-engineers @security-team
# GitHub Actions 工作流 DevSecOps 团队必须审查
/.github/workflows/ @devsecops-team
# Kubernetes manifests
/k8s/ @platform-team
```
当 PR 涉及这些路径时,指定的团队会被**自动添加为必需的评审者**。
### 4.7 RBAC 谁可以在你的代码库中做什么
并非每个人都需要相同的访问级别。
| 角色 | 访问级别 | 适用对象 |
|------|-------------|-------------|
| **Admin** | 完全控制权:设置、删除代码库、管理成员 | DevOps 负责人、代码库所有者 |
| **Maintain** | 管理 Issues、PR、设置(无破坏性操作) | 高级工程师 |
| **Write** | 推送分支、创建 PR、合并(需批准) | 所有开发者 |
| **Triage** | 管理 Issues 和 PR,不能推送代码 | QA、项目经理 |
| **Read** | 仅查看和克隆 | 外部承包商、审计员 |
```
GitHub Repo → Settings → Collaborators and teams → Manage access
```
### 4.8 Dependabot 自动依赖更新
你的应用程序使用了第三方包。这些包存在漏洞。Dependabot 会监视你的依赖项,并**自动创建 PR** 来更新有漏洞的包。
```
# .github/dependabot.yml
version: 2
updates:
# Node.js / npm
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "security"
# Python / pip
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
# Docker base images
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
```
Dependabot 会:
1. 扫描你的 `package.json`、`requirements.txt`、`Dockerfile` 等。
2. 对照 GitHub Advisory Database 检查已知的 CVE
3. 自动打开一个带有修复的 PR
4. 向你展示漏洞的严重程度
## 5. IaC 安全 Terraform
Infrastructure as Code (IaC) 意味着你的云基础设施——服务器、数据库、网络——是在代码文件(通常是 Terraform `.tf` 文件)中定义的。这很强大,但也带来了新的安全风险。
### 常见的 IaC 安全错误
| 错误 | 真实世界的影响 |
|---------|------------------|
| 在 `.tf` 文件中硬编码 AWS 凭证 | 攻击者获得完整的 AWS 访问权限 |
| 创建 S3 存储桶时设为 `public = true` | 所有公司数据暴露在互联网上 |
| 安全组在端口 22 上开放 `0.0.0.0/0` | 任何人都可以通过 SSH 连接到你的服务器 |
| 将 `terraform.tfstate` 提交到 Git | 包含所有资源 ID、IP,有时还包含秘密信息 |
| RDS 数据库未加密 | 如果存储被攻破,将导致数据泄露 |
### 5.1 不要这样做
```
# ❌ 绝对不要这样做 硬编码凭证
provider "aws" {
region = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE" # ← hardcoded AWS key
secret_key = "wJalrXUtnFEMI/K7MDENG" # ← hardcoded secret
}
# ❌ 绝对不要这样做 公开的 S3 存储桶
resource "aws_s3_bucket_acl" "data" {
acl = "public-read" # ← entire bucket exposed to internet
}
# ❌ 绝对不要这样做 对全世界开放的 SSH
resource "aws_security_group_rule" "ssh" {
cidr_blocks = ["0.0.0.0/0"] # ← anyone can SSH in
from_port = 22
to_port = 22
protocol = "tcp"
}
```
### 5.2 Checkov 自动化 IaC 配置错误扫描器
Checkov 是一个开源工具,可以根据**数百项安全最佳实践**扫描你的 Terraform(和其他 IaC)文件。
把它看作是一个用于安全的 linter。
```
# 安装
pip install checkov
# 扫描你的 terraform 目录
checkov -d ./terraform
# 扫描单个文件
checkov -f main.tf
# 输出为 JSON 以进行 CI 集成
checkov -d ./terraform --output json
```
**示例输出:**
```
Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
FAILED for resource: aws_s3_bucket.data
File: /terraform/s3.tf:12-20
Check: CKV_AWS_21: "Ensure the S3 bucket has versioning enabled"
FAILED for resource: aws_s3_bucket.data
File: /terraform/s3.tf:12-20
Passed checks: 18, Failed checks: 4, Skipped checks: 0
```
**将 Checkov 添加到你的 CI 流水线中**
```
# .github/workflows/iac-security.yaml
name: IaC Security Scan
on:
pull_request:
paths:
- 'terraform/**'
jobs:
checkov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
output_format: sarif
output_file_path: checkov-results.sarif
soft_fail: false # fail the pipeline if issues found
- name: Upload results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: checkov-results.sarif
```
### 5.3 HashiCorp Vault CI/CD 的机密管理
#### 在 GitHub Secrets 中存储凭证的问题
GitHub Secrets 看似安全,但是:
- 它们是**长期有效的**——同一个密钥可以使用数月或数年
- 如果有人获得了代码库设置的访问权限,他们就可以看到这些机密
- 没有关于谁在何时使用了该机密的审计追踪
- 轮换机密需要手动在每个代码库中进行更新
#### 解决方案:通过 Vault + OIDC 获取短期凭证
```
The Flow:
Developer → Push code → GitHub Actions starts
↓
GitHub sends JWT token to Vault
(using OIDC OpenID Connect protocol)
↓
Vault verifies: "Is this really GitHub Actions
running on repo X, branch Y?"
↓
Vault generates short-lived AWS credentials
(valid for 15 minutes only)
↓
GitHub Actions uses credentials to deploy
↓
Credentials automatically expire
(even if stolen, they are useless after 15 min)
```
**使用 Vault 的 GitHub Actions 工作流:**
```
# .github/workflows/deploy.yaml
name: Deploy Infrastructure
on:
push:
branches: [main]
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get short-lived credentials from Vault
uses: hashicorp/vault-action@v3
with:
url: https://vault.your-company.com
method: jwt
role: github-actions-deploy
secrets: |
aws/creds/deploy-role access_key | AWS_ACCESS_KEY_ID ;
aws/creds/deploy-role secret_key | AWS_SECRET_ACCESS_KEY
- name: Deploy with Terraform
run: |
terraform init
terraform apply -auto-approve
# AWS credentials are now available as environment variables
# They will expire in 15 minutes automatically
```
**针对 Terraform 的 `.gitignore` 最佳实践:**
```
# Terraform 绝对不要提交这些
*.tfstate
*.tfstate.backup
.terraform/
.terraform.lock.hcl
terraform.tfvars # often contains real values
*.auto.tfvars
crash.log
override.tf
override.tf.json
```
## 6. 容器安全 Docker
容器就像是一台轻量级的虚拟机,它将你的应用程序及其所有依赖项打包在一起。Docker 是构建和运行容器最流行的工具。
### 为什么容器安全很重要
当你运行一个 Docker 容器时,它与宿主机共享**相同的 Linux 内核**。如果容器被攻破,攻击者可能会逃逸到宿主机。
### 6.1 切勿以 Root 身份运行容器
默认情况下,Docker 容器内的进程以 `root` 用户身份运行。这就像以系统管理员身份运行整个应用程序一样。
**为什么这很危险:**
```
Container running as root
↓
Attacker exploits a vulnerability in your app
↓
Attacker has root inside the container
↓
Docker daemon runs as root on the host
↓
Attacker escapes container → gets root on the HOST machine
↓
Complete infrastructure compromise
```
**❌ 不安全的 Dockerfile(以 root 身份运行):**
```
FROM node:25
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["npm", "start"]
# 没有 USER 指令 = 默认以 root 运行
```
**✅ 安全的 Dockerfile(非 root 用户):**
```
FROM node:25
WORKDIR /app
# 创建专用的非 root 用户和组
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
COPY . .
RUN npm install
# 在 CMD 之前切换到非 root 用户
USER appuser
EXPOSE 3000
CMD ["npm", "start"]
```
### 6.2 多阶段构建 更小的镜像,更小的攻击面
容器内的每个包都是一个潜在的漏洞。包越多,攻击面就越大。
**单阶段构建的问题:**
```
FROM node:25 ← Full Node.js image: 1.1 GB
WORKDIR /app
COPY . .
RUN npm install ← Installs build tools + dev dependencies
EXPOSE 3000
CMD ["npm", "start"]
Final image size: ~400 MB
Contains: build tools, compilers, dev dependencies, npm cache
All of these are unnecessary at runtime and are potential vulnerabilities.
```
**多阶段构建将过程分为两个阶段:**
```
Stage 1 (Build): Install everything needed to BUILD the app
Stage 2 (Run): Copy only the final artifact nothing else
```
**✅ 多阶段 Dockerfile:**
```
# ─────────────────────────────────────────
# 阶段 1:构建阶段
# ─────────────────────────────────────────
FROM node:25 AS builder
WORKDIR /build
# 首先复制依赖清单(Docker 层缓存优化)
# 如果 package.json 未更改,Docker 会复用缓存的 npm install 层
COPY package.json package-lock.json ./
RUN npm ci --only=production # ci is faster and more reliable than install
# 复制源代码
COPY . .
# ─────────────────────────────────────────
# 阶段 2:生产阶段
# ─────────────────────────────────────────
FROM node:25-slim
WORKDIR /app
# 仅从构建阶段复制我们需要的内容
COPY --from=builder /build/node_modules ./node_modules
COPY --from=builder /build/app.js ./app.js
EXPOSE 3000
CMD ["node", "app.js"]
# 结果:约 80 MB,而不是约 400 MB
# 构建工具、编译器和开发依赖不在最终镜像中
```
### 6.3 Distroless 镜像 最精简的基础镜像
即使是 `node:25-slim` 也包含一个 shell(`bash`/`sh`)、包管理器(`apt`)和系统实用工具。进入容器的攻击者可以使用这些工具。
**Distroless 镜像**只包含:
- 应用程序运行时(例如 Node.js)
- 应用程序本身
- 没有其他任何东西——没有 shell,没有包管理器,没有实用工具
```
# ─────────────────────────────────────────
# 阶段 1:构建(需要完整工具)
# ─────────────────────────────────────────
FROM node:25 AS builder
WORKDIR /build
# 在此处创建非 root 用户(distroless 没有 shell 来执行此操作)
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
COPY package.json package-lock.json ./
RUN npm ci --only=production
COPY . .
# ─────────────────────────────────────────
# 阶段 2:Distroless 生产镜像
# ─────────────────────────────────────────
FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
# 从 builder 复制用户定义(distroless 没有 useradd)
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group
# 仅复制应用文件
COPY --from=builder /build/node_modules ./node_modules
COPY --from=builder /build/app.js ./app.js
# 以非 root 身份运行
USER appuser
EXPOSE 3000
CMD ["app.js"]
# 最终镜像大小:约 52 MB
# 没有 shell = 攻击者即使进入也无法运行命令
```
**镜像大小比较:**
| 基础镜像 | 大小 | 有 Shell | 有包管理器 |
|-----------|------|-----------|-------------------|
| `node:25` | ~1.1 GB | ✅ 是 | ✅ 是 |
| `node:25-slim` | ~80 MB | ✅ 是 | ✅ 是 |
| `gcr.io/distroless/nodejs20` | ~52 MB | ❌ 否 | ❌ 否 |
### 6.4 `.dockerignore` 不要将秘密信息复制到你的镜像中
`COPY . .` 指令会将项目目录中的**所有内容**复制到镜像中。这包括:
- 带有密码的 `.env` 文件
- `.git/` 目录(提交历史)
- `node_modules/`(已在构建阶段安装)
- `Dockerfile` 本身
- 测试文件、文档
```
# .dockerignore
# Secrets 绝对不要包含这些
.env
.env.*
*.pem
*.key
secrets/
# Git 历史记录
.git
.gitignore
# 依赖项(将在镜像中全新安装)
node_modules/
__pycache__/
.venv/
# 构建产物
dist/
build/
*.log
# 开发文件
*.test.js
*.spec.js
tests/
docs/
# Docker 文件本身
Dockerfile*
docker-compose*
.dockerignore
```
### 6.5 加固的 `docker run` 运行时安全
即使拥有了安全的镜像,你也可以在运行时添加额外的保护:
```
docker run \
--read-only \ # Container filesystem is read-only
--tmpfs /tmp \ # Allow writes only to /tmp (in memory)
--cap-drop ALL \ # Drop ALL Linux capabilities
--cap-add NET_BIND_SERVICE \ # Add back only what you need
--security-opt no-new-privileges \ # Process cannot gain more privileges
--pids-limit 100 \ # Max 100 processes (prevents fork bombs)
--memory 256m \ # Max 256 MB RAM
--cpus 0.5 \ # Max 50% of one CPU core
--user 1001:1001 \ # Run as specific UID:GID
-p 3000:3000 \
your-image:tag
```
**每个标志的作用:**
| 标志 | 保护作用 |
|------|-----------|
| `--read-only` | 攻击者无法将恶意软件写入文件系统 |
| `--tmpfs /tmp` | 允许合法的临时写入而不进行持久化 |
| `--cap-drop ALL` | 移除所有 Linux 内核能力(root 权限) |
| `--security-opt no-new-privileges` | 防止通过 setuid 二进制文件进行权限提升 |
| `--pids-limit` | 防止导致宿主机崩溃的 fork 炸弹攻击 |
| `--memory` / `--cpus` | 防止资源耗尽攻击 |
### 6.6 Trivy 扫描镜像中的 CVE
在将镜像推送到生产环境之前,请扫描其已知漏洞:
```
# 安装 Trivy
brew install aquasecurity/trivy/trivy # macOS
# 或者
apt install trivy # Ubuntu
# 扫描本地镜像
trivy image your-app:latest
# 扫描并在发现高危或严重漏洞时报错
trivy image --exit-code 1 --severity HIGH,CRITICAL your-app:latest
# 在 CI pipeline 中扫描
trivy image --format sarif --output trivy-results.sarif your-app:latest
```
**添加到 GitHub Actions:**
```
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: your-app:latest
format: sarif
output: trivy-results.sarif
severity: HIGH,CRITICAL
exit-code: 1 # Fail pipeline if vulnerabilities found
- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
```
## 7. Kubernetes 安全
Kubernetes (K8s) 是大规模运行和管理容器的平台。大多数工程师会直接跳到创建 EKS 集群并部署 pod。而 DevSecOps 工程师会首先考虑**整个安全架构**。
### 设置 EKS 的正确方法(对比常见方法)
```
❌ Common approach:
Create EKS cluster → Deploy pods → Add security later
✅ DevSecOps approach:
VPC (Virtual Private Cloud)
└─ Private Subnets (nodes are NOT accessible from internet)
└─ EKS Cluster
└─ Node Groups (EC2 instances in private subnets)
└─ Pods (your applications)
└─ Secrets managed by Vault / AWS Secrets Manager
```
关键的区别在于:**节点位于私有子网中**。你的集群节点没有直接的互联网访问权限。所有流量都通过负载均衡器或 VPN 进行。
### 7.1 Namespaces 团队之间的隔离
默认情况下,Kubernetes 集群中的所有 pod 都可以相互看到并进行交互。当多个团队共享一个集群时,这是一个问题。
**没有命名空间的问题:**
```
Cluster (no namespaces)
├── payments-pod-1
├── payments-pod-2
├── shipment-pod-1
├── scores-pod-1
└── scores-configmap
Problem: A developer on the payments team can accidentally
delete scores-configmap. There is no ownership boundary.
```
**解决方案:带有 ResourceQuotas 的命名空间:**
```
# namespace-setup.yaml
# 为 payments 团队创建隔离的 namespace
apiVersion: v1
kind: Namespace
metadata:
name: team-payments
labels:
team: payments
environment: production
---
# 限制 payments 团队可以使用的 CPU 和内存量
apiVersion: v1
kind: ResourceQuota
metadata:
name: payments-quota
namespace: team-payments
spec:
hard:
# Minimum resources pods must request
requests.cpu: "4"
requests.memory: 8Gi
# Maximum resources pods can use
limits.cpu: "10"
limits.memory: 16Gi
# Maximum number of pods
pods: "20"
# Maximum number of services
services: "10"
```
```
kubectl apply -f namespace-setup.yaml
# 现在部署到特定的 namespace
kubectl apply -f deployment.yaml -n team-payments
# team-payments 中的开发人员不会意外影响 team-shipment
```
### 7.2 Kubernetes 中的 RBAC 细粒度权限
Kubernetes RBAC 控制 pod 或用户可以对**哪些资源**执行**什么操作**。
这三个组件:
```
ServiceAccount → Role → RoleBinding
(WHO) (WHAT) (connects WHO to WHAT)
```
**ServiceAccount** = pod 的身份(类似于用户账户,但用于应用程序)
**Role** = 一组权限(对什么资源执行什么动作)
**RoleBinding** = 将 Role 附加到 ServiceAccount
**真实示例:支付 Pod 只能读取(READ)Pod,而不能修改它们**
```
# 步骤 1:为 payments 应用创建一个 ServiceAccount
kubectl create serviceaccount payments-app -n team-payments
```
```
# 步骤 2:创建具有最小权限的 Role
# role-pod-reader.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: team-payments
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"] # READ ONLY no create, delete, update
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"] # Can read configmaps but not modify
```
```
# 步骤 3:将 Role 绑定到 ServiceAccount
# rolebinding-payments.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: payments-pod-reader
namespace: team-payments
subjects:
- kind: ServiceAccount
name: payments-app
namespace: team-payments
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
```
```
kubectl apply -f role-pod-reader.yaml
kubectl apply -f rolebinding-payments.yaml
# 验证权限
kubectl auth can-i list pods \
--as=system:serviceaccount:team-payments:payments-app \
-n team-payments
# 输出:yes
kubectl auth can-i delete pods \
--as=system:serviceaccount:team-payments:payments-app \
-n team-payments
# 输出:no
```
**ClusterRole vs Role:**
| 类型 | 范围 | 用例 |
|------|-------|----------|
| `Role` | 单个命名空间 | 应用需要读取其自己命名空间中的 pod |
| `ClusterRole` | 所有命名空间 | 监控工具需要读取所有地方的 pod |
### 7.3 Network Policies 控制 Pod 间的流量
默认情况下,**每个 pod 都可以与集群中的其他所有 pod 通信**。这意味着如果攻击者攻破了你的前端 pod,他们就可以直接连接到你的数据库 pod。
Network Policies 允许你在 pod 级别定义防火墙规则。
```
# network-policy-payments-db.yaml
# 仅允许 payments-api pod 连接到 payments-db pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: payments-db-access
namespace: team-payments
spec:
# This policy applies to pods with label: app=payments-db
podSelector:
matchLabels:
app: payments-db
policyTypes:
- Ingress # Control incoming traffic
- Egress # Control outgoing traffic
ingress:
# Only allow traffic FROM pods with label: app=payments-api
- from:
- podSelector:
matchLabels:
app: payments-api
ports:
- protocol: TCP
port: 5432 # PostgreSQL port
egress:
# Database pod should not initiate outbound connections
- {} # Empty = deny all egress
```
```
kubectl apply -f network-policy-payments-db.yaml
# 现在:
# payments-api → payments-db: 允许(端口 5432)
# frontend → payments-db: 阻止
# shipment-api → payments-db: 阻止
```
### 7.4 Kyverno 使用 Admission Controller 进行策略执行
**问题:** 一名初级工程师使用 `image: mysql:latest` 部署了一个 pod。今天它运行良好。明天,`latest` 标签获得了一个严重的 CVE。你的生产数据库现在变得脆弱不堪。
**Admission Controllers** 会在每个发送到 Kubernetes API 的请求被保存之前将其拦截。Kyverno 允许你编写策略来:
- **验证**:阻止违反你规则的资源
- **变更**:自动添加标签、注解或安全设置
- **生成**:自动创建相关资源
```
# 安装 Kyverno
kubectl apply --server-side \
-f https://github.com/kyverno/kyverno/releases/latest/download/install.yaml
```
**策略 1:阻止 `latest` 镜像标签**
```
# policy-no-latest-tag.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
annotations:
policies.kyverno.io/description: >
Require all container images to have a specific version tag.
The 'latest' tag is mutable and makes deployments unpredictable.
spec:
validationFailureAction: Enforce # Enforce = block, Audit = just warn
rules:
- name: require-image-tag
match:
resources:
kinds:
- Pod
validate:
message: "Image tag 'latest' is not allowed. Use a specific version like 'mysql:8.0.35'."
pattern:
spec:
containers:
- image: "!*:latest"
```
**策略 2:要求使用非 root 容器**
```
# policy-no-root-containers.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-non-root
spec:
validationFailureAction: Enforce
rules:
- name: check-runAsNonRoot
match:
resources:
kinds:
- Pod
validate:
message: "Containers must not run as root. Set runAsNonRoot: true."
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: true
```
**策略 3:自动变更——如果缺少安全上下文则自动添加**
```
# policy-add-security-context.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-security-context
spec:
rules:
- name: add-security-context
match:
resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
securityContext:
+(allowPrivilegeEscalation): false
+(readOnlyRootFilesystem): true
```
### 7.5 Kubernetes 中的机密管理
Kubernetes 有一个内置的 `Secret` 资源,但它有一个致命的缺陷:**机密仅仅是 base64 编码的,而不是加密的**。
```
# 任何拥有 kubectl 访问权限的人都可以在几秒钟内解码 secret
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
# 输出:SuperSecret123!
```
**正确的方法:External Secrets Operator (ESO) + HashiCorp Vault**
不要将实际的机密存储在 Kubernetes 中,而是存储一个指向机密在 Vault 中位置的**引用**。ESO 会在运行时获取真实的机密。
```
What you store in Git (safe to commit):
┌─────────────────────────────────────┐
│ ExternalSecret manifest │
│ "Get the DB password from │
│ vault/secret/payments/db" │
└─────────────────────────────────────┘
What ESO does at runtime:
ExternalSecret → ESO → Vault → fetches real secret → creates K8s Secret
```
**步骤 1:安装 ESO**
```
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace
```
**步骤 2:将 Vault 配置为 SecretStore**
```
# vault-secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: team-payments
spec:
provider:
vault:
server: "https://vault.your-company.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "payments-app"
```
**步骤 3:创建一个 ExternalSecret(可安全提交到 Git)**
```
# external-secret-db.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: payments-db-credentials
namespace: team-payments
spec:
refreshInterval: 1h # Re-fetch from Vault every hour
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: db-credentials # Name of the K8s Secret to create
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: secret/payments/database
property: password
- secretKey: username
remoteRef:
key: secret/payments/database
property: username
```
```
# 此文件可以安全地提交到 Git 它不包含实际的 secrets
kubectl apply -f external-secret-db.yaml
# ESO 通过从 Vault 获取自动创建真实的 K8s Secret
kubectl get secret db-credentials -n team-payments
```
## 8. CI/CD 流水线安全
CI/CD 流水线是一条自动化的装配线,它将代码从开发者的提交带到生产环境中运行的应用程序。DevSecOps 工程师将这条流水线变成一道**安全门**——代码除非通过每项安全检查,否则无法到达生产环境。
### 安全流水线架构
```
Developer pushes code
↓
┌─────────────────────────────────────────────────────────────┐
│ CI/CD PIPELINE │
│ │
│ Stage 1: Secret Scanning (Gitleaks) │
│ ↓ PASS │
│ Stage 2: SAST Static Code Analysis (SonarQube/Semgrep) │
│ ↓ PASS │
│ Stage 3: SCA Dependency Scanning (Snyk/OWASP Dependency) │
│ ↓ PASS │
│ Stage 4: Build Docker Image │
│ ↓ │
│ Stage 5: Container Scanning (Trivy) │
│ ↓ PASS │
│ Stage 6: IaC Scanning (Checkov) │
│ ↓ PASS │
│ Stage 7: Deploy to Staging │
│ ↓ │
│ Stage 8: DAST Dynamic Testing (OWASP ZAP) │
│ ↓ PASS │
│ Stage 9: Deploy to Production │
└─────────────────────────────────────────────────────────────┘
```
任何阶段失败都会**停止流水线**。代码将不会继续向前推进。
### 完整的安全流水线示例
```
# .github/workflows/secure-pipeline.yaml
name: Secure CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
permissions:
contents: read
security-events: write # Required for uploading SARIF results
id-token: write # Required for OIDC with Vault
jobs:
# ─────────────────────────────────────────
# Stage 1: Secret Scanning
# ─────────────────────────────────────────
secret-scan:
name: Secret Scanning
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ─────────────────────────────────────────
# Stage 2: SAST Static Code Analysis
# ─────────────────────────────────────────
sast:
name: Static Code Analysis
runs-on: ubuntu-latest
needs: secret-scan
steps:
- uses: actions/checkout@v4
- name: Run Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/python
generateSarif: "1"
- name: Upload SAST results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep.sarif
# ─────────────────────────────────────────
# Stage 3: SCA Dependency Scanning
# ─────────────────────────────────────────
sca:
name: Dependency Vulnerability Scan
runs-on: ubuntu-latest
needs: secret-scan
steps:
- uses: actions/checkout@v4
- name: Run Snyk SCA
uses: snyk/actions/python@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --sarif-file-output=snyk.sarif
- name: Upload SCA results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: snyk.sarif
# ─────────────────────────────────────────
# Stage 4 & 5: Build + Container Scan
# ─────────────────────────────────────────
build-and-scan:
name: Build and Scan Container
runs-on: ubuntu-latest
needs: [sast, sca]
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t app:${{ github.sha }} .
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: HIGH,CRITICAL
exit-code: 1
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
# ─────────────────────────────────────────
# Stage 6: IaC Scanning
# ─────────────────────────────────────────
iac-scan:
name: IaC Security Scan
runs-on: ubuntu-latest
needs: secret-scan
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
soft_fail: false
# ─────────────────────────────────────────
# Stage 7: Deploy to Staging
# ─────────────────────────────────────────
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: [build-and-scan, iac-scan]
environment: staging
steps:
- uses: actions/checkout@v4
- name: Get credentials from Vault
uses: hashicorp/vault-action@v3
with:
url: ${{ secrets.VAULT_URL }}
method: jwt
role: github-deploy-staging
secrets: |
aws/creds/staging access_key | AWS_ACCESS_KEY_ID ;
aws/creds/staging secret_key | AWS_SECRET_ACCESS_KEY
- name: Deploy to staging
run: kubectl apply -f k8s/ -n staging
# ─────────────────────────────────────────
# Stage 8: DAST Dynamic Testing
# ─────────────────────────────────────────
dast:
name: Dynamic Application Security Testing
runs-on: ubuntu-latest
needs: deploy-staging
steps:
- name: Run OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: https://staging.your-app.com
rules_file_name: zap-rules.tsv
cmd_options: '-a' # include alpha passive scan rules
# ─────────────────────────────────────────
# Stage 9: Deploy to Production
# ─────────────────────────────────────────
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: dast
environment: production # Requires manual approval in GitHub
steps:
- uses: actions/checkout@v4
- name: Get production credentials from Vault
uses: hashicorp/vault-action@v3
with:
url: ${{ secrets.VAULT_URL }}
method: jwt
role: github-deploy-production
secrets: |
aws/creds/production access_key | AWS_ACCESS_KEY_ID ;
aws/creds/production secret_key | AWS_SECRET_ACCESS_KEY
- name: Deploy to production
run: kubectl apply -f k8s/ -n production
```
## 9. 应用安全 SAST, DAST, SCA
这三种测试方法涵盖了应用安全的不同角度。它们共同构成了完整的全景。
```
SAST → Reads your source code without running it (like proofreading)
SCA → Checks your dependencies for known CVEs (like checking ingredients)
DAST → Attacks your running application (like a real hacker)
```
### 9.1 SAST 静态应用安全测试
SAST 在不执行的情况下分析你的**源代码**。它会寻找指示安全漏洞的模式。
**SAST 能发现什么:**
- SQL 注入漏洞
- 跨站脚本攻击 (XSS)
- 硬编码凭证
- 不安全的加密方式(MD5、SHA1)
- 路径遍历漏洞
- 不安全的反序列化
**SAST 无法发现什么:**
- 只在运行时出现的漏洞
- 业务逻辑缺陷
- 依赖于配置的身份验证问题
#### 工具:Semgrep
Semgrep 是一个快速、开源的 SAST 工具,支持 30 多种语言。
```
# 安装
pip install semgrep
# 使用 OWASP Top 10 规则进行扫描
semgrep --config=p/owasp-top-ten ./src
# 扫描 Python 代码
semgrep --config=p/python-security ./src
# 使用多个规则集进行扫描
semgrep --config=p/security-audit --config=p/secrets ./src
```
**示例:Semgrep 捕获 SQL 注入**
```
# ❌ 易受攻击的代码 Semgrep 会标记此项
def get_user(username):
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query) # SQL Injection vulnerability
# ✅ 安全的代码 参数化查询
def get_user(username):
query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))
```
#### 工具:SonarQube
SonarQube 是一个带有 Web 仪表盘的企业级 SAST 平台。
```
# sonar-project.properties
sonar.projectKey=my-app
sonar.projectName=My Application
sonar.sources=src
sonar.language=py
sonar.python.coverage.reportPaths=coverage.xml
```
```
# 在 GitHub Actions 中
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
```
### 9.2 SCA 软件组成分析
现代应用程序中有 80% 是第三方代码(库、框架、包)。SCA 会根据已知漏洞(CVE)数据库检查你使用的每一个依赖项。
**SCA 解决的问题:**
```
Your app uses: requests==2.25.0
↓
CVE-2023-32681 discovered in requests < 2.31.0
↓
Without SCA: You never know. Your app is vulnerable.
With SCA: Pipeline fails. You get a PR to update to 2.31.0.
```
#### 工具:Snyk
```
# 安装
npm install -g snyk
# 认证
snyk auth
# 测试 Python 项目
snyk test --file=requirements.txt
# 测试 Node.js 项目
snyk test
# 持续监控(在发现新 CVE 时发送警报)
snyk monitor
```
**Snyk 示例输出:**
```
✗ High severity vulnerability found in django
Description: SQL Injection
Info: https://snyk.io/vuln/SNYK-PYTHON-DJANGO-2314115
Introduced through: django@3.2.0
Fix: Upgrade django to 3.2.14 or higher
✗ Medium severity vulnerability found in pillow
Description: Buffer Overflow
Introduced through: pillow@8.3.1
Fix: Upgrade pillow to 9.0.0 or higher
2 vulnerabilities found (1 high, 1 medium)
```
#### 工具:OWASP Dependency-Check(免费替代方案)
```
# 运行依赖项检查
dependency-check --project "My App" --scan ./src --format HTML
# 在 GitHub Actions 中
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'my-app'
path: '.'
format: 'SARIF'
out: 'reports'
args: --failOnCVSS 7 # Fail if CVSS score >= 7 (High)
```
### 9.3 DAST 动态应用安全测试
DAST 是最真实的安全测试。它**切实攻击你正在运行的应用程序**——就像真正的黑客一样——发送恶意输入,探测端点,测试身份验证。
**SAST 无法发现但 DAST 可以发现的内容:**
- 身份验证和会话管理缺陷
- 服务器配置错误
- 只在特定输入下出现的漏洞
- 业务逻辑缺陷
- API 安全问题
**DAST 工作流:**
```
1. Deploy app to staging environment
2. DAST tool crawls the application (finds all pages/endpoints)
3. DAST tool sends attack payloads to each endpoint:
- SQL Injection attempts
- XSS payloads
- Path traversal attempts
- Authentication bypass attempts
4. DAST tool reports which attacks succeeded
5. Pipeline fails if critical vulnerabilities found
```
#### 工具:OWASP ZAP (Zed Attack Proxy)
OWASP ZAP 是使用最广泛的开源 DAST 工具。
**基线扫描(被动——无主动攻击,对任何环境安全):**
```
# .github/workflows/dast.yaml
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: 'https://staging.your-app.com'
# Baseline scan: crawls and passively checks for issues
# Does NOT send attack payloads safe for staging
```
**完整扫描(主动——发送真实攻击载荷,仅限预发环境):**
```
- name: ZAP Full Scan
uses: zaproxy/action-full-scan@v0.10.0
with:
target: 'https://staging.your-app.com'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a -j'
# Full scan: actively tests for SQL injection, XSS, etc.
# ONLY run against staging NEVER against production
```
**自定义 ZAP 规则文件 (`.zap/rules.tsv`):**
```
# 规则 ID 动作 描述
10016 IGNORE Web Browser XSS Protection Not Enabled (handled by CDN)
10020 IGNORE X-Frame-Options Header (handled by CDN)
40012 WARN Cross Site Scripting (Reflected)
40014 FAIL Cross Site Scripting (Persistent)
40018 FAIL SQL Injection
90022 FAIL Application Error Disclosure
```
### 9.4 SAST vs DAST vs SCA 快速参考
| | SAST | SCA | DAST |
|--|------|-----|------|
| **测试什么** | 你的源代码 | 你的依赖项 | 你运行中的应用 |
| **何时运行** | 构建时 | 构建时 | 部署后 |
| **需要运行中的应用吗?** | 否 | 否 | 是 |
| **能发现** | 代码漏洞 | 包中已知的 CVE | 运行时漏洞 |
| **速度** | 快(秒级) | 快(秒级) | 慢(分钟级) |
| **误报率** | 高 | 低 | 中 |
| **最佳工具** | Semgrep, SonarQube | Snyk, OWASP Dep-Check | OWASP ZAP |
## 10. 终极安全流水线
将所有内容整合在一起——这是按层组织的完整 DevSecOps 安全检查清单。
### 各层安全检查清单
#### Git 安全
- [ ] .gitignore 已配置用于过滤机密、状态文件、凭证
- [ ] 已安装带有 Gitleaks 的 pre-commit 框架
- [ ] .pre-commit-config.yaml 已提交到代码库
- [ ] 已添加 Gitleaks GitHub Actions 工作流
- [ ] main 分支已启用分支保护规则
- [ ] PR 至少需要 2 名评审者
- [ ] 已配置 CODEOWNERS 文件
- [ ] 已配置 RBAC (Admin / Write / Read)
- [ ] 已为所有包生态系统启用 Dependabot
#### IaC 安全
- [ ] .tf 文件中没有硬编码的凭证
- [ ] terraform.tfstate 已添加到 .gitignore
- [ ] CI 中对每个 Terraform PR 运行 Checkov
- [ ] 已配置 HashiCorp Vault 用于短期凭证
- [ ] GitHub Actions 使用 OIDC(而非长期密钥)
#### 容器安全
- [ ] Dockerfile 使用非 用户
- [ ] 实现了多阶段构建
- [ ] 使用了 Distroless 或 slim 基础镜像
- [ ] 已配置 .dockerignore
- [ ] CI 流水线中运行 Trivy 扫描
- [ ] docker run 使用了 --read-only、--cap-drop ALL 以及资源限制
#### Kubernetes 安全
- [ ] EKS 节点位于私有子网中
- [ ] 按团队创建了命名空间
- [ ] 每个命名空间设置了 ResourceQuotas
- [ ] RBAC:ServiceAccount 仅具有最小权限
- [ ] Network Policies 限制了 Pod 间的流量
- [ ] Kyverno 策略:禁止 latest 标签、禁止 root 容器
- [ ] 使用 External Secrets Operator + Vault 管理机密
- [ ] 没有将原始 Kubernetes Secrets 提交到 Git
#### 应用安全
- [ ] CI 流水线中包含 SAST (Semgrep/SonarQube)
- [ ] CI 流水线中包含 SCA (Snyk/OWASP Dep-Check)
- [ ] 每次部署后对预发环境运行 DAST (OWASP ZAP)
- [ ] 所有 HIGH/CRITICAL 级别的发现都会阻断流水线
### 工具参考
| 类别 | 工具 | 用途 | 成本 |
|----------|------|---------|------|
| 机密扫描 | Gitleaks | Pre-commit + CI 扫描 | 免费 |
| 机密扫描 | GitHub Secret Scanning | 自动化代码库扫描 | 免费 |
| IaC 扫描 | Checkov | Terraform 配置错误 | 免费 |
| 机密管理 | HashiCorp Vault | 动态短期凭证 | 免费 (OSS) |
| 容器扫描 | Trivy | 镜像的 CVE 扫描 | 免费 |
| 容器 Lint | Hadolint | Dockerfile 最佳实践 | 免费 |
| SAST | Semgrep | 源代码漏洞扫描 | 免费 (OSS) |
| SAST | SonarQube | 企业级代码质量与安全 | 免费 (社区版) |
| SCA | Snyk | 依赖项漏洞扫描 | 免费 (受限) |
| SCA | OWASP Dep-Check | 依赖项 CVE 扫描 | 免费 |
| DAST | OWASP ZAP | 动态应用测试 | 免费 |
| K8s 策略 | Kyverno | Admission Controller 策略 | 免费 |
| K8s 机密 | External Secrets Operator | 从 Vault 同步机密到 K8s | 免费 |
| 威胁建模 | OWASP Threat Dragon | 可视化威胁建模 | 免费 |
以安全第一的理念构建。
每一次提交都是一个安全决策。让它发挥作用。到此结束。来自 Mathanprasath K 的衷心告别!标签:Chrome Headless, CI/CD流水线, CISA项目, DAST, DevSecOps, Docker, ECS, Git加固, IaC扫描, Kubernetes RBAC, Kubernetes安全, SAST, StruQ, Terraform, Web截图, 上游代理, 威胁建模, 子域名突变, 安全指南, 安全防御评估, 实战教程, 容器安全, 左移安全, 恶意软件分析, 盲注攻击, 网络安全研究, 请求拦截, 零到生产