安全编程(安全编程)的核心思路是把"安全"从"个人意识"变成"工程约束":不指望每个开发者每次都记得做对,而是让做错的事情在流程里根本走不通——判断标准很朴素:一条安全要求,只有在它能"让构建失败"的时候,才算真的被落实了。
4-4 到 4-10 讲的都是"具体某个漏洞怎么防",这一节讲的是它们背后的共同做法:怎么让这些防法"自动发生"。
这一节也是本章里唯一一个"面向开发者"的立场——前面几节你是防守方(要打补丁、要看日志),这一节你是产出方(要写出不需要别人打补丁的东西)。 教材把它放在第四章接近结尾的位置,意思很清楚:与其一个一个修,不如从一开始就不产生。
一、核心思路:从"意识"到"闸门"
"大家要注意安全"这句话是最没用的安全措施——因为它的效果完全依赖于"每个人每次都不忘",而人一定会忘。
工程化的做法是把安全要求变成"闸门":在流程里设一个检查点,不通过就过不去。 这样做的效果是:安全不再依赖个人的记性,而变成系统属性的一部分。
2026 年最成体系的一套目标来自 CISA 的 Secure by Design 承诺(到 2026 年初,签署方已经超过 350 家)。它列了七个目标,下面把它们逐个翻译成"能被检查、能让发布失败"的工程闸门:
| 目标 | 可测的指标 | 不达标怎么办 |
|---|---|---|
| 多因子认证 | 默认开启率 | 新增租户不开 MFA 不放行 |
| 默认口令 | 首发即须改口令的产品数 | 该项不归零就不发版 |
| 整类漏洞消除 | 某一类弱点的存量计数 | 引入该类新问题的合入请求直接拒 |
| 补丁安装率 | 严重漏洞中位修复天数 | 互联网产品超过 14 天即告警 |
| 漏洞披露政策 | 是否发布 VDP 与 security.txt |
没有就不能过合规审 |
| CVE 完整性 | 已修漏洞是否都发了编号 | 漏发编号算未完成 |
| 入侵证据 | 基础包是否含安全日志 | 默认不带日志不予发布 |
这张表的关键在最后一列:每一个目标都对应一个"能让构建失败"的条件。这就是"闸门"和"口号"的区别——口号是"我们提倡",闸门是"你不做,代码就合不进去"。
二、为什么要"消除整类",而不是"一个一个修"
七个目标里最需要解释的是"整类漏洞消除"——它和"修漏洞"的区别在哪?
"修漏洞"是:发现一个、修一个——问题是漏洞会不断被找到,你永远在追。 "消除整类"是:某一种写法在你们的代码库里被禁止——比如"所有 SQL 必须参数化""所有密码必须用特定算法哈希""所有输出必须经过模板转义",于是这一类漏洞从"逐个修"变成"不再产生"。
这个思路的实际效果,在 CISA 2026 年的复盘里有直接的证据:
- 大多数入侵不是国家级零日,而是犯罪分子扫描暴露出来的已知漏洞;
- KEV 目录里有 41.5% 的条目属于"顽固弱点"——也就是 MITRE CWE Top 25 里几乎每年都会出现的同一批老毛病;
- 按 2007 年的"不可原谅漏洞"标准,今天最常见的 10 个弱点里仍有 3 个当年就算不可原谅(当时的判定条件是:写法已有定论、攻击路径明确、利用极其简单)。
这三条合起来的意思很直接: 问题不在"防不住新攻击",而在"同一类错误反复犯"——二十年前就已经有定论的事情,今天还在漏洞榜上。
三、动手 Demo:把七个目标变成七条闸门
下面这段脚本把七个目标、它们对应的可测指标、以及"不达标怎么处理"排成一张表:
gates = [
("多因子认证", "默认开启率", "新增租户不开 MFA 不放行"),
("默认口令", "首发即须改口令的产品数", "该项不归零就不发版"),
("整类漏洞消除", "某一类弱点的存量计数", "引入该类新问题的合入请求直接拒"),
("补丁安装率", "严重漏洞中位修复天数", "互联网产品超过 14 天即告警"),
("漏洞披露政策", "是否发布 VDP 与 security.txt", "没有就不能过合规审"),
("CVE 完整性", "已修漏洞是否都发了编号", "漏发编号算未完成"),
("入侵证据", "基础包是否含安全日志", "默认不带日志不予发布"),
]
for goal, metric, action in gates:
print(goal, "|", metric, "|", action)
实跑输出:
CISA 的 Secure by Design 承诺有七个目标(2026 年初已超 350 家签署方)
下面把每个目标翻译成一条【能被检查、能让发布失败】的工程闸门。
一、七个目标,翻成七条闸门
目标 可测的指标 不达标怎么办
----------------------------------------------------------------------------------------------
多因子认证 默认开启率 新增租户不开 MFA 不放行
默认口令 首发即须改口令的产品数 该项不归零就不发版
整类漏洞消除 某一类弱点的存量计数 引入该类新问题的合入请求直接拒
补丁安装率 严重漏洞中位修复天数 互联网产品超过 14 天即告警
漏洞披露政策 是否发布 VDP 与 security.txt 没有就不能过合规审
CVE 完整性 已修漏洞是否都发了编号 漏发编号算未完成
入侵证据 基础包是否含安全日志 默认不带日志不予发布
→ 一个承诺只有在能【让构建失败】时,才不只是一份新闻稿。
二、CISA 2026 年的复盘说明了什么
① 大多数入侵不是国家级零日,而是犯罪分子扫描暴露的已知漏洞
② KEV 里有 41.5% 的条目属于【顽固弱点】——
也就是 MITRE CWE Top 25 里几乎每年都出现的同一批老毛病
③ 按 2007 年的标准,今天最常见的 10 个弱点里有 3 个
当年就算【不可原谅】(写法有定论、攻击路径明确、利用极简单)
→ 问题不在防不住,而在【同一类错反复犯】。
三、买家这一侧的动作(Secure by Demand)
采购时值得问的四个问题:
① 你们已经【消除】了哪些漏洞类别?
② 是否提供机读的软件物料清单(SBOM)?
③ 默认是否提供抗钓鱼的认证方式?
④ 安全日志是否包含在基础产品里(不用另外买)?
读这个输出:
- 第一张表最后一列的措辞值得逐条体会:"不放行""不发版""直接拒""即告警""不能过合规审""算未完成""不予发布"——每一条都是"有后果的动作"。这就是"闸门"和"建议"的区别:建议可以不做,闸门做不过去。
- 第二部分的第 ② 条是这一节最有说服力的数字:"KEV 里 41.5% 属于顽固弱点"。这个数字说明:被实际利用的漏洞里,将近一半是那种"早就知道该怎么防、却一直重复出现"的老问题——这正是"消除整类"这个思路的价值所在: 它不是"更努力地修",而是"不再产生"。
- 第三部分换了一个视角:如果开发者这一侧不容易推动,就从采购这一侧推。"Secure by Demand"的含义是:买家把安全要求写进采购条件——厂商为了拿到订单就必须满足。这套逻辑对普通用户的启发是: 你在选软件、选设备、选服务时问的那几个问题,本身就是在推动对方把事情做好。
四、⚠️ 2026 现状
- 【"默认安全"已经从理念变成了可以审计的产品属性】。 七个目标里有三个直接和"默认值"有关(MFA 默认开启、不要默认口令、默认带日志)。这个共同点是刻意的: 默认值是"用户什么都不做时会发生什么",而绝大多数用户确实什么都不做。所以"安全"这件事最有效的杠杆不是"提供选项",而是"让默认的那个选项就是安全的"——这也是 4-6 里"浏览器把 SameSite 默认设成 Lax"能产生那么大效果的原因。
- 【"漏洞披露政策"成了软件产品的标配,而且正在变成硬要求】。 第七条要求厂商对外说明"收到漏洞报告后怎么处理"(典型形式是 VDP 页面和
security.txt文件)。2026 年的进展是这件事在往法定要求走:欧盟的网络弹性法案从 2026 年 9 月 11 日起,对主动被利用的漏洞规定了 24 小时预警、72 小时通知、14 天最终报告的硬时限(4-14)。这意味着"要不要公开处理漏洞"不再是一个态度问题,而是一个合规问题。 - 【供应链已经单独成为一个高位风险类别】。 OWASP Top 10:2025 里新增了"软件供应链失效"这一项(4-12),对应到开发者这一侧的具体要求就是"软件物料清单(SBOM)":你要能说清"你的产品里装了哪些第三方组件、各是什么版本"。这件事的现实驱动力来自 2026 年的一类事故: 第三方组件的更新通道本身被攻击(比如插件的自动更新被投毒),于是"保持更新"这个动作变成了风险来源。没有 SBOM,你在出事后连"哪些客户受影响"都答不出来。
- 【"入侵证据"这一条最容易被忽略,但它决定后面所有事】。 "基础包默认带安全日志"看起来只是一条配置要求,但它的连带影响很大:没有日志,"有没有被入侵"就永远无法回答;没有日志,"攻击者是从哪个漏洞进来的"也无从判断。CISA 2026 年的复盘里那句"大多数入侵来自扫描已知漏洞"之所以能被算出来,前提就是有人留下了可分析的日志。所以这一条的现实含义是: 安全不只是在"挡",还在"看得见"——一个挡不住但看得见的系统,比一个挡不住又看不见的系统要好一个量级。
- 【"整类消除"在语言和框架层面已经有了明确的抓手】。 最典型的是内存安全(4-8):用内存安全语言写新代码,等于把整类内存漏洞从"要不要小心"变成"写不出来"。而其他几类也都有对应的抓手:SQL 注入对应"参数化"(4-4)、XSS 对应"框架的默认输出转义"(4-5)、CSRF 对应"浏览器默认的 SameSite"(4-6)。共同规律是: 最有效的防法不在"开发者要记得",而在"平台/语言/框架把这个错误变得不容易犯"。
30 秒实验
今天做两个动作,各 15 秒:
- 打开你常用的任何一个软件或服务,看它的"安全"设置页,只问一个问题:那个最关键的开关(比如两步验证)现在是"默认开着"还是"默认关着"。如果默认是关的,说明这个产品把"安全"当成了一个可选项——而"默认安全"才是这一节讲的七个目标的方向。 这一步的意义是让你有一个判断产品的尺子: 一个真正重视安全的产品,不会把最重要的开关默认关掉。
- 如果你参与过任何软件的开发或验收,今天回想一个"某次忘了做某件安全的事"的例子。然后问自己:那件事有没有可能做成一条"做不过去"的检查(比如提交前自动检查、发布流水线里加一步、模板里默认开启)。把这件"靠记性"的事变成"靠流程"的事——这就是安全编程最小、也最可复制的动作。
这一节要带走的一句是:安全编程不是"让每个人都记住更多规则",而是"让错误的做法在流程里走不通"——判断一条安全要求有没有被真正落实,就看它能不能让构建失败。
常见误区
- 错误:安全是安全团队的事,开发者只管把功能写出来。——纠正:这个分工在 2026 年已经越来越不成立了。证据来自几个方向:① OWASP Top 10:2025 里排最前的"访问控制失效""安全配置错误"都是纯粹的实现问题(不是"安全团队配置错了",而是"代码写错了");② "安全左移"这个词的流行本身就说明业界共识:在开发阶段修一个漏洞的成本,远低于上线之后再修;③ 而"消除整类漏洞"这个目标,只有写代码的人才能做到——安全团队能发现和推动,但不能替研发改掉那一行拼接的 SQL。 所以准确的分工是: 安全团队负责"定标准、做检测、追踪进度",开发者负责"让代码本身不出这类问题"。
- 错误:我们做的是内部系统,不需要这么讲究。——纠正:"内部"这个前提经常不成立,而且它的后果往往更大。原因有两层: ① 攻击者的路径通常是"先拿到一个不重要的入口,再横向移动到重要的地方"(2026 年复盘里"大多数入侵来自扫描已知漏洞"讲的就是这种打法的起点);② 内部系统往往权限更大、日志更少、补丁更滞后——它们通常是"防守最松但价值最高"的那一类。 这也是本节把"入侵证据"(默认带日志)列为七个目标之一的原因: 内部系统最需要的恰恰是"出事时能被看见"。
- 错误:第三方组件是别人维护的,出了问题不怪我们。——纠正:站在用户的角度,责任在你这里。你的产品把它打进了包、你的更新机制把它推给了用户,那么"你用了什么"就是你的责任。2026 年的插件投毒事故(4-12:25 个插件被投毒、约 80 万以上站点受影响)里,受影响的站点并没有"做错什么"——它们只是"安装了插件"。所以正确的做法是两条: ① 用软件物料清单(SBOM)知道自己用了什么;② 把"依赖的更新"也纳入审查流程(不是所有自动更新都该无条件放行)。
- 错误:我们把安全写进了制度文件,就算落实了。——纠正:制度文件是"口号"的书面形式,它和"闸门"的区别就是本节的判断标准:能不能让构建失败。一份写着"所有 SQL 必须参数化"的规范,如果没有任何检查手段去执行它,它的实际效果等于零。把它变成闸门的做法很具体:把静态检查工具接进流水线(发现拼接 SQL 就报错)、把依赖扫描接进流水线(发现高危组件就拦住发布)、把"新增租户必须开启 MFA"写成代码里的默认值。所以这一节最后那句判断标准值得反复用: "这条要求能不能让构建失败?"——不能的话,它还只是一个愿望。
自测三问(点开对答案)
1. "闸门"和"口号"的区别,用一句话怎么说? —— **闸门有后果,口号没有**。**具体到操作上:闸门是"这条检查不通过,代码就合不进去、版本就发不出去";口号是"我们提倡大家注意安全"。** **这个区别之所以重要,是因为它决定了这件事"依赖什么"**:**口号依赖每个人的记性和责任心(一定会失效),闸门依赖流程本身(不通过就是过不去)。** **本节那张表之所以要专门列出"不达标怎么办"这一列,就是在把每个目标从口号改造成闸门**——**这也是判断一份安全方案是否认真的最快方式:看它有没有"不达标怎么办"。** 2. 为什么"KEV 里 41.5% 属于顽固弱点"这个数字,支持了"消除整类漏洞"这个思路? —— **因为它说明被实际利用的漏洞里,将近一半不是"新奇的攻击",而是"早就知道怎么防的老毛病"**。**"顽固弱点"的定义就是"MITRE CWE Top 25 里几乎每年都会出现的同一批问题"**——**换句话说:这些漏洞已经被记录了十几年,防御方法早有定论,但它们仍然在被写出来、被利用。** **这就说明"一个一个修"是不够的**:**你今年修掉的这个问题,明年会在另一段代码里再出现一次**(**因为根本原因是"那种写法被允许"**)。**所以更有效的做法是从源头禁止那种写法**(**换语言、用参数化、让框架默认转义、把检查接进流水线**)——**这就是"消除整类",也是这七个目标里技术含量最高的一条。** 3. 为什么把"安全日志默认包含在基础产品里"也算一条安全目标? —— **因为它决定了"出了事之后你能不能知道、能不能追查"**。**安全的完整链条是"预防 → 检测 → 响应 → 复盘",日志是后面三环的前提**。**没有日志:** **你无法回答"我被入侵了吗"(检测不了)、"攻击者是从哪里进来的"(响应不了)、"这个漏洞影响了多少客户"(复盘不了)**。**这也是 CISA 2026 年复盘能得出"大多数入侵来自扫描已知漏洞"这个结论的前提**——**没有日志,这种统计根本做不出来。** **更实际的一点是:** **日志常常被厂商当成"高级版功能"来卖**,**而这一条要求正是要把它拉回基础包里**——**因为"看不见"比"挡不住"更危险:挡不住至少还有机会被检测到,看不见则连机会都没有。**- ☐ 下一节(4-12 当心那些使用方便的工具中存在的漏洞)
相关笔记
- 防范针对 Web 应用的典型攻击手段 —— 上一篇:代码修不及时的临时办法
- 当心那些使用方便的工具中存在的漏洞 —— 下一篇:插件与内容管理系统
- 信息安全的三要素 —— 纵深防御与"看不见"的风险
- 内存缓冲区溢出的滥用 —— "消除整类"最典型的例子
- 🛡️ 网络与信息安全 · 索引 —— 全课路线图