元数据卡
- 前置知识:第4章(Web 安全)、第5章(操作系统安全)、常规软件开发流程
- 预计时间:50 分钟
- 核心难度:进阶
- 完成标志:能解释 SDL 的各个阶段,能用 STRIDE 模型分析一个系统的威胁,能区分 SAST、DAST、IAST 的适用场景
你的进度
你已经逐层构筑了魔法驿道的防线:加密咒语、符文证书体系、Web 应用护盾、操作法阵隔离、魔力管道监控。
但有一个问题没有解决:所有这些防御知识,每次都得在出现了漏洞之后才去学。上一个 SQL 注入修好了,下一个 XSS 又冒出来了。你需要的不是逐个漏洞打补丁,而是一个系统性的方法——在法力咒语还没写的时候就想清楚安全风险。
安全不是运维加一个 WAF 就行,安全是一个工程流程问题。 你的任务
理解安全开发生命周期(SDL)——如何把安全嵌入到软件开发的每个阶段而不是留到最后。你将掌握威胁建模(STRIDE)、安全测试(SAST/DAST/IAST)、依赖管理和安全响应计划。
本章分层
- 必读:SDL 七阶段、STRIDE 威胁建模(含实例)、SAST vs DAST
- 选读:SCA(软件组成分析)、DeviSecOps 实践
- 进阶:OWASP ASVS 标准、Supply Chain Levels for Software Artifacts (SLSA)
破局 · 溯源
问题:你回顾这段时间的经历——每一个漏洞几乎都是事后发现的。SQL 注入是上线后扫出来的,XSS 是用户报告说"你们的页面弹奇怪东西"才发现的,SSH 端口暴露在外网是渗透测试发现的。
问题不在于"没有发现漏洞",而在于"发现得太晚"。越晚发现安全漏洞,修复成本越高:
需求阶段发现:修复成本 1x
设计阶段发现:修复成本 6x
实现阶段发现:修复成本 15x
测试阶段发现:修复成本 30x
运维阶段发现:修复成本 100x+核心教训:安全不能是最后一道工序。它必须是嵌入到每个阶段的需求。
第一块:SDL(安全开发生命周期)
SDL 是微软在 2000 年代初提出的方法论。它的核心是把安全活动加入到软件开发的标准流程中:
需求阶段 ─→ 设计阶段 ─→ 实现阶段 ─→ 验证阶段 ─→ 发布阶段 ─→ 响应阶段
│ │ │ │ │ │
├─安全需求 ├─威胁建模 ├─SAST ├─DAST ├─安全审查 ├─漏洞应急
├─安全培训 ├─安全设计 ├─依赖检查 ├─渗透测试 ├─配置基线 ├─热修复
├─攻击面分析 ├─安全编码 ├─合规检查 │
└─签名 + SBOM需求阶段:
- 确定安全需求:系统需要满足哪些安全标准?
- 数据分类:系统处理什么类型的数据?PII(个人身份信息)?信用卡(PCI)?
- 安全培训:开发团队有没有足够的安全知识?
设计阶段——STRIDE 威胁建模:
STRIDE 是由微软提出的一种威胁分类模型,让你在系统设计时就系统性地找出安全问题。
| 类别 | 威胁 | 被破坏的安全属性 | 示例 |
|---|---|---|---|
| Spoofing | 假冒身份 | 认证 | 伪造 JWT 签名 |
| Tampering | 篡改数据 | 完整性 | 修改 HTTP 请求体 |
| Repudiation | 否认操作 | 不可抵赖性 | 用户否认发起了某个操作 |
| Information Disclosure | 信息泄露 | 机密性 | 日志中输出密码 |
| Denial of Service | 拒绝服务 | 可用性 | 频繁请求压垮 API |
| Elevation of Privilege | 权限提升 | 授权 | 普通用户执行管理员操作 |
用 STRIDE 分析"巡逻报告系统":
系统描述:一个 REST API,前端展示巡逻报告,后端连接 PostgreSQL 数据库
数据流图(简化):
用户 ─→【前端】──HTTP──→【API 服务】──SQL──→【数据库】
│
└──→【日志服务】
STRIDE 分析每个组件:
【前端】
S: 攻击者伪造其他用户的 session
T: 修改前端 JS 逻辑
R: 用户否认提交了内容(需要访问日志记录)
I: 在 URL 中暴露 session ID
D: 大量并发请求导致前端不可用
E: 通过 XSS 提升到同站其他用户的权限
【API 服务】
S: 伪造 JWT 签名(如果使用弱算法 "none")
T: 通过 SQL 注入篡改数据库
R: 没有请求日志,用户否认操作
I: 错误信息暴露堆栈跟踪和表结构
D: 高并发 SQL 查询耗尽数据库连接池
E: API 端缺少权限检查,越权查看他人报告
【数据库】
S: 允许无密码的本地 socket 连接
T: 用弱权限用户连接,可修改系统数据
I: 存储了明文密码
D: 连接数未限制,被耗尽
E: 数据库用户有 DROP TABLE 权限
【日志服务】
S: 伪造日志条目
T: 日志被外部写入(日志注入)
R: 日志被删除,无法追溯
I: 日志包含敏感数据(token/密码)
D: 日志文件撑爆磁盘完成 STRIDE 分析后,为每个找到的威胁分配优先级和应对措施:
| 威胁 | 严重性 | 应对 | 措施 |
|---|---|---|---|
| SQL 注入 | 高 | 缓解 | 参数化查询 |
| 越权访问 | 高 | 缓解 | 增加权限检查 |
| JWT 弱算法 | 中 | 缓解 | 强制 RS256,校验算法白名单 |
| 日志包含 token | 中 | 缓解 | 日志过滤器自动脱敏 |
| 明文密码存储 | 高 | 缓解 | 用 Argon2 哈希 |
| 数据库 DROP 权限 | 中 | 缓解 | 最小权限用户 |
第二块:安全测试工具
SAST(Static Application Security Testing)
SAST 在代码层面做静态分析,不需要运行程序。像 spellcheck 但检查的是安全漏洞。
# SonarQube(社区版支持 Java/C#/Python/JS 等)
sonar-scanner \
-Dsonar.projectKey=patrol-report \
-Dsonar.sources=. \
-Dsonar.host.url=http://localhost:9000
# Bandit(Python 专用)
pip install bandit
bandit -r src/ -f json -o bandit-report.json
# SpotBugs + FindSecBugs(Java)
mvn compile
mvn com.github.spotbugs:spotbugs-maven-plugin:spotbugsSAST 能检测到的问题:
- 硬编码的密码和密钥
- SQL 注入(检测字符串拼接)
- 不安全的随机数使用
- 危险函数调用(
eval()、exec()) - 过期的依赖库
DAST(Dynamic Application Security Testing)
DAST 从外部测试正在运行的应用。它模拟攻击者的视角——不知道源码,通过发送恶意请求来探测漏洞。
# OWASP ZAP(最流行的开源 DAST)
docker run -u zap -p 8080:8080 -i ghcr.io/zaproxy/zaproxy \
zap.sh -port 8080 -host 0.0.0.0
# 被动扫描(通过代理监听流量)
# 主动扫描
docker run -v $(pwd):/zap/wrk/:rw -t ghcr.io/zaproxy/zaproxy \
zap-cli quick-scan --self-contained \
--start-options '-config api.disablekey=true' \
http://localhost:8081/supply-app/DAST 能检测到的问题:
- XSS(反射型和存储型)
- CSRF
- 不安全的 HTTP 头
- SQL 注入(通过请求触发)
- 目录遍历
- Open redirect
SAST vs DAST:
| 维度 | SAST | DAST |
|---|---|---|
| 需要源码 | 是 | 否 |
| 需要运行应用 | 否 | 是 |
| 发现阶段 | 实现阶段 | 测试阶段 |
| 误报率 | 较高(需要人工确认) | 较低(触发式验证) |
| 覆盖范围 | 所有代码路径 | 运行时可达的路径 |
| 配置复杂度 | 低 | 中(需要应用运行环境) |
| 典型工具 | SonarQube, Bandit, Checkmarx | OWASP ZAP, Burp Suite, Acunetix |
IAST(Interactive Application Security Testing,交互式应用安全测试):
结合 SAST 和 DAST 的优点——在运行的应用中植入探针,在 DAST 扫描的从内部监控代码执行路径。
DAST 从外部发送测试请求
│
▼
应用运行中 ──→ IAST 探针捕获代码执行路径
│ │
▼ ▼
返回结果 分析实际执行的代码路径
标记有漏洞的代码IAST 的优势:它知道哪些代码路径被实际执行了,所以比 SAST 少很多误报。它也能检测到 DAST 无法覆盖的深层逻辑(如 DAST 无法看到的业务逻辑漏洞)。
第三块:依赖管理和供应链安全
现代应用 90% 以上的代码来自第三方依赖。一个被攻破的依赖等于给你的要塞开了一扇后门。
SCA(Software Composition Analysis):
# OWASP Dependency-Check
dependency-check --scan target/ --format HTML
# npm audit(JavaScript 依赖检查)
npm audit --json
# Trivy(支持多种语言和容器镜像)
trivy fs src/
trivy image my-app:latestSCA 的核心是检查已知漏洞数据库(CVE / NVD)——如果你的依赖库有一个 CVE-2023-xxxxx,SCA 会在 CI 中阻止发布。
SBOM(软件物料清单,Software Bill of Materials):
SBOM 是一个清单,列出你的软件中所有组件和依赖。在供应链攻击(如 SolarWinds 事件)后,SBOM 已经是关键基础设施的基本要求。
{
"$schema": "https://raw.githubusercontent.com/CycloneDX/specification/master/schema/bom-1.4.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"components": [
{
"type": "library",
"name": "fastapi",
"version": "0.104.0",
"purl": "pkg:pypi/fastapi@0.104.0",
"licenses": [{"license": {"id": "MIT"}}],
"hashes": [{"alg": "SHA-256", "content": "..."}]
}
]
}生成 SBOM:
# Python 项目
pip install cyclonedx-bom
cyclonedx-py requirements.txt --output bom.json
# Node.js 项目
npm install -g @cyclonedx/cyclonedx-npm
cyclonedx-npm --output-file bom.json第四块:漏洞响应
即使做了所有预防,漏洞仍然会发生。关键是发生后的响应速度。
发现漏洞
│
├── 评估严重性(CVSS 评分)
│ CVSS 3.1:AccessVector + AttackComplexity + PrivilegesRequired + UserInteraction
│ → Critical (9.0-10.0) / High (7.0-8.9) / Medium (4.0-6.9) / Low (0.1-3.9)
│
├── 制定修复方案
│ Critical:24小时内修复上线
│ High:1周内
│ Medium:在下一个发布周期修复
│
├── 应用补丁 / 热修复
│
├── 发布安全通告(如果影响用户)
│
└── 事后复盘(Root Cause Analysis)
└── 更新 SDL 流程,防止同类漏洞再次出现用 Python 快速获取已知漏洞信息:
import requests
# NVD API(National Vulnerability Database)
def get_cve_details(cve_id):
url = f"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve_id}"
resp = requests.get(url)
data = resp.json()
cve = data['vulnerabilities'][0]['cve']
metrics = cve.get('metrics', {})
# 取 CVSS 3.1 评分
cvss_v3 = metrics.get('cvssMetricV31', [{}])[0].get('cvssData', {})
return {
'id': cve_id,
'description': cve['descriptions'][0]['value'],
'severity': cvss_v3.get('baseSeverity'),
'score': cvss_v3.get('baseScore'),
'vector': cvss_v3.get('vectorString'),
}
print(get_cve_details("CVE-2021-44228")) # Log4Shell常见陷阱
- 安全测试只在发布前做一次。 安全是持续的过程,不是节前大扫除。CI/CD 中集成 SAST/DAST,每次提交都检查。
- 漏扫就是安全测试。 很多组织的"安全测试"只是跑一次漏洞扫描器。这和真正的安全工程相差甚远——威胁建模、渗透测试、代码审查、依赖性分析都要覆盖。
- 认为云服务提供商负责安全。 云安全是共享责任模型(Shared Responsibility Model)。AWS/Azure/GCP 负责云基础设施的安全("云的安全"),你负责在云中部署的应用的安全("云中的安全")。
- 依赖扫描只做一次。 依赖的漏洞是持续发现的。
npm audit或trivy应该在每次 CI 构建中运行。 - 没有安全回退计划。 当你发现一个严重的 0-day 漏洞,你的团队知道先做什么吗?有没有一个 playbook?关键人员有没有联系方式?——这些应该在安全响应计划中预先定义。
- 忽略业务逻辑漏洞。 扫描器可以发现 SQL 注入和 XSS,但扫描器发现不了"用户可以给自己转账"或者"用户可以退货超过购买数量"这种业务逻辑缺陷。这些需要威胁建模、代码审查和渗透测试。
通关挑战
- 热身:对一个你熟悉的内部系统,用 STRIDE 模型做一次完整的威胁建模。识别出至少 5 个潜在的威胁,并按严重性排列优先级。
- 挑战:在 CI 管道中集成 SAST 和依赖检查。用 GitHub Actions 或 GitLab CI,加入 Bandit(Python)或 SonarQube,以及 Trivy 依赖扫描。配置成:严重漏洞(Critical/high)阻断合并(MR)。
- 观察:运行 OWASP ZAP 对本地运行的 web 应用做一次被动扫描,阅读扫描报告,找到至少一个漏洞并确认修复。
- 排障:你发现 SAST 扫描报告中有 100 个告警。你先处理了 10 个关键告警,但剩下的 90 个低风险告警占满了后续 Sprint。如何制定一个策略来逐步消除这些告警?(提示:可以考虑新代码零容忍 + 遗留债务分级处理)
旅人笔记
- SDL 把安全嵌入到需求、设计、开发、测试、发布、响应每个阶段
- 威胁建模(STRIDE)是设计阶段发现安全问题的系统化方法
- SAST(静态分析)在代码阶段发现问题;DAST(动态测试)在运行时模拟攻击
- IAST 结合两者优势,在实际执行路径上检测漏洞
- SCA 和 SBOM 解决供应链安全——知道你的软件里有什么依赖
- 漏洞响应计划必须在漏洞发生之前就准备好
下一站预告
你学完了如何保护系统。最后一个问题:系统里存储的用户数据怎么办?数据不是代码——它有法律人格,有合规要求。下一章,我们看隐私与数据保护——GDPR、数据匿名化、数据生命周期管理。