一个功能上线了。安全评审随后才做,渗透测试发现了问题,或者更糟——一封事故报告躺进了你的收件箱。漏洞是真的,修复很贵。然后有人说了一句:"我们得给这个加上安全。"
这句话本身就是问题所在。安全不是往软件上"加"的东西,而是从第一天就"建"进去的东西。当你开始讨论给一个已经存在的东西补安全时,你已经处于损失控制模式了。
Secure by Design 是另一套思路:在做架构决策之前先定义安全需求,在写第一个类之前先理解威胁模型,在领域层识别并强制执行业务规则。这些都属于工程本身该做的事,而不是事后给已建成的东西打的一层补丁。
被动补漏和主动设计,差在哪
大多数团队是被动处理安全的:东西坏了,被标记了,打个补丁。循环往复。系统就这样积累技术性的安全债,和积累其他技术债一样,只是代价更高。
Secure by Design 的团队是主动的。功能开建之前,工程团队先问:这个功能面临什么威胁?数据在哪里敏感?如果攻击者控制了某个输入会怎样?某个组件被攻破后的爆炸半径有多大?答案塑造架构,而不是反过来。
这样产出的系统从根上不同。不是单个组件不同,而是每一层的设计决策都反映了对"哪里会出错"的理解。比如:API 网关默认限流,因为设计时考虑到了滥用;领域层强制执行业务规则,因为设计时明确了这些规则该放在哪;CI/CD 流水线能抓出代码里的密钥,因为设计时就把这道关卡算了进去。
在设计阶段把安全做对的成本,只是事后修复或管理成本的一小部分。而管理成本,又只是重大事故成本的一小部分。
威胁建模:动手之前先看清攻击面
威胁建模是在系统建成之前,系统性地识别哪里会出错。这是一项正式的工程活动,不是安全团队的专属练习。目标很简单:搞清楚谁可能攻击你的系统、他们想要什么、可能怎么拿到、得手后影响多大。有了这些理解,你才能做出有意为之的设计决策,降低攻击发生的可能性和影响。
它围绕四个问题展开。第一个问题是"我们在建什么":先画出系统——有哪些组件、它们怎么通信、数据怎么流动、信任边界在哪里。信任边界就是数据从一个信任语境跨到另一个语境的地方。API 网关和互联网之间、服务层和数据库之间、你的系统和第三方集成之间,都存在信任边界。每一条信任边界都是潜在的攻击面。把数据流画出来,标出数据进入、存储、处理和离开的每一个位置,你才能看清自己在保护什么。
第二个问题是"哪里会出错"。对每个组件、每条数据流,问攻击者能做什么。这里可以用 STRIDE 这套框架来逐项排查:
- 仿冒:攻击者能否伪装成别人?能否伪造令牌、冒充用户、声称一个不属于自己的身份?
- 篡改:攻击者能否修改传输中或存储中的数据?能否改动交易金额、修改回调载荷、篡改已存记录?
- 抵赖:用户能否否认自己做过某个操作?是否有足够的审计日志证明发生了什么、何时发生、由谁操作?
- 信息泄露:攻击者能否获知不该知道的东西?能否读取其他用户的数据、从错误响应里提取内部系统信息、从堆栈跟踪里发现内部基础设施细节?
- 拒绝服务:攻击者能否阻止合法用户访问系统?能否耗尽资源、触发昂贵操作、用流量压垮系统?
这套问题清单的价值在于,它把"安全"从一个模糊的担忧,拆成了可以逐条对着架构图检查的具体项。CI/CD 流水线能抓住人工评审漏掉的东西,而每一层的设计都带着攻击者的视角——不是事后补的,是基础要求。
这件事不是安全团队的责任,是工程团队的责任。而且它从第一个 pull request 之前就开始了。
热门跟贴