作为架构师,我们正处在“左移”这条时间轴的最前端。因为,我们是构建安全体系的第一责任人,也是最重要的防线。
引言:正在被黑客“侵蚀”的系统

在前面的文章中,我们探讨如何构建高并发、高可用、可扩展的系统。我们如同专业的建筑师,设计了宏伟的“数字大厦”,确保它能抵御流量的洪峰,容忍硬件的故障,并能灵活地扩展。然而,如果我们忽略了一个基础而致命的问题,那么我们精心建造的一切,都可能在一夜之间,毁于一旦。这个问题,就是安全。
在传统的软件开发模式中,安全往往扮演着一个令人尴尬的“挨打了才知道疼”的角色。我们称之为“安全右移”的反模式:
研发团队日常都在紧锣密鼓的开发,偶发的有SQL注入的攻击、类似等保通保的检查,都是以应付的方式去打补丁,只要不出大事即可。但是如果你的公司规模要扩大,比如扩张到海外市场,或者要IPO。正所谓「树大招风」,各种问题就会接踵而至,而且不查还好,问题是越查越多....
为了修复这些问题,团队不得不集中精力花费大量时间进行伤筋动骨的修改。原定的发布计划被打乱,研发与安全团队之间充满了紧张和抱怨。整个过程,就像是等到大楼已经起火,才匆忙派来一辆救火车,手忙脚乱地进行补救。
这种反模式的弊端显而易见:
-
修复成本极高:在软件生命周期的末端发现并修复一个安全漏洞,其成本是设计阶段的数十倍甚至上百倍。
-
延误业务时机:临门一脚的安全问题,是导致产品无法按时上线、错失市场窗口的重要原因。
-
滋生团队矛盾:安全团队成了“拦路虎”,研发团队成了“闯祸者”,彼此缺乏信任。
今天,我们要探讨的,正是颠覆这一落后模式的先进理念——“安全&合规左移”。其核心思想非常简单:将安全&合规,从流程的终点(右侧),大幅度地向左移动,将其融入到需求分析、架构设计、编码、测试的每一个环节中去。
本文不是要把架构师变成专业的安全专家,而是要帮助大家建立起“安全始于设计”的理念,并掌握一套行之有效的威胁建模方法论。我们将学习如何在架构层面,系统性地思考认证授权、数据加密、访问控制等关键问题,从而在设计图纸完成的那一刻,就将绝大多数潜在的安全风险,消弭于无形。
一、 思维转变:从“亡羊补牢”到“未雨绸缪”

“安全&合规左移”首先是一种思维模式的转变。它要求我们彻底抛弃“安全是安全部门的事”这种旧观念,将安全内化为架构设计的一部分,如同性能、可用性一样,成为我们与生俱来的设计约束。
1. 安全&合规,是一种质量属性,而非一项功能
与“高性能”、“可扩展”等类似,安全&合规也是软件质量属性最重要的部分,不能把它看作一个独立的功能,在项目最后去“添加”它。我们知道,性能是由架构、算法、数据结构共同决定的。同理,安全也不是一个可以最后“打补丁”的功能。一个系统的安全水位,从根本上是由其架构决定的。例如:
-
服务间的通信方式(HTTP vs. HTTPS)
-
用户身份的验证机制(简单的Session vs. OAuth 2.0)
-
核心数据的存储方式(明文 vs. 加密)
这些在架构设计阶段做出的决策,直接决定了系统的“攻击面”和“防御纵深”。一个在设计上就存在缺陷的系统,无论后期投入多少WAF(Web应用防火墙)、IDS(入侵检测系统)等昂贵的“外挂”安全设备,都如同在一个漏洞百出的大厦外围增加再多的门锁,也无法从根本上解决问题。
2. 架构师的责任:构建“默认安全”的系统
作为架构师,我们的目标是构建一个“默认安全&合规”的系统。这意味着,即使开发者犯了一些常见的错误,系统本身的架构设计,也能提供一层“兜底”的保护。
-
反例(依赖个人):要求每个开发者在每个SQL查询前,都必须手动进行参数防注入处理。这极度依赖人的自觉性,百密必有一疏。
-
正例(架构保障):在架构层面,强制要求所有数据库访问,都必须通过一个统一的、内置了防SQL注入逻辑的数据访问层(DAO/Repository)。这样,开发者根本没有机会写出不安全的代码。
这种从“依赖人的自觉”到“依靠体系的保障”的转变,正是“安全&合规左移”在架构设计中的核心体现。
通常,在国内的互联网企业中,安全与合规这两块业务的重合比例高,很多都是同一个团队负责的,合规会在审计和法律监管等方面有比较多的要求,单独列出来也有非常多的内容,由于篇幅原因,我们这里后面的内容都以安全为例,以讲述思路为主,合规的部分可以后续专门整理讨论。
二、 架构师的安全工具箱:需要关注的核心领域

要做到“安全始于设计”,架构师需要在绘制架构图时,脑海中始终装着一张“安全地图”。这张地图包含以下几个核心领域:
1. 认证:你是谁?
-
核心问题:系统如何可靠地验证用户、服务、设备的身份?
-
架构思考:
-
用户认证:是否采用了业界标准的协议,如OAuth 2.0 / OIDC?密码存储是否使用了加盐的哈希算法?是否提供了多因素认证(MFA)的能力?
-
服务间认证:微服务之间相互调用时,如何确认对方是合法的内部服务,而不是一个被渗透后伪装的恶意服务?是否采用了mTLS(双向TLS)或基于JWT的令牌认证?
-
2. 授权:你能做什么?
-
核心问题:身份验证通过后,该身份被允许执行哪些操作?
-
架构思考:
-
权限模型:采用RBAC(基于角色的访问控制)还是ABAC(基于属性的访问控制)?权限逻辑是硬编码在业务代码里,还是由一个统一的授权中心来管理?
-
最小权限原则:这是安全&合规设计中最核心的原则之一。默认情况下,任何身份都应该只有完成其任务所必需的、最小的权限集合。绝不能为了图方便,就给一个普通服务赋予“管理员”权限。
-
3. 数据安全:如何保护信息资产?
-
核心问题:如何保护系统中最宝贵的资产——数据?
-
架构思考:
-
传输中(In-Transit):所有网络通信,无论是公网还是内网,是否都强制使用了TLS加密?
-
静止时(At-Rest):存储在数据库、对象存储、磁盘文件中的敏感数据(如用户个人信息、支付信息),是否进行了加密存储?
-
使用中(In-Use):在业务逻辑处理数据时,如何避免敏感信息泄露?例如,在日志中,绝不能打印用户的密码、身份证号等明文信息。对于特别敏感的数据,是否考虑在内存中使用后立即销毁?
-
4. 输入验证与输出编码
-
核心问题:如何防止恶意用户通过输入数据来攻击系统?
-
架构思考:
-
“永不信任用户输入”:这是Web安全的铁律。架构上,是否有一个统一的入口层(如API网关),对所有外部输入,进行格式、长度、类型的校验?
-
防止注入类攻击:如上文所述,是否通过框架或数据访问层的封装,系统性地杜绝了SQL注入、命令注入的可能?
-
防止跨站脚本(XSS):在将数据输出到前端页面时,是否有一个统一的模板引擎或框架,默认对所有输出进行HTML编码,防止恶意脚本的执行?
-
5. 依赖安全
-
核心问题:我们引用的第三方开源库,是否安全?
-
架构思考:
-
现代应用是构建在海量开源组件之上的“巨人”。任何一个底层依赖库的漏洞(如著名的Log4Shell),都可能让整个系统瞬间崩溃。
-
架构师有责任推动建立一套软件成分分析(SCA)机制,定期扫描项目依赖,识别已知的漏洞,并制定升级或替换策略。
-
三、 核心方法论:威胁建模 —— 像“黑客”一样思考

有了“地图”,我们还需要一个“导航系统”,来引导我们系统性地发现潜在风险。这个导航系统,就是威胁建模(Threat Modeling)。
威胁建模是一个结构化的过程,它帮助我们在设计阶段,系统性地回答四个核心问题:
-
我们正在构建什么?(What are we building?)
-
它可能会出现什么问题?(What can go wrong?)
-
我们应该如何应对?(What are we going to do about it?)
-
我们做得足够好了吗?(Did we do a good enough job?)
一个简单有效的威胁建模流程,可以分为以下四步:
第一步:分解系统
首先,画出你的系统架构图。这可以是我们在前面文章中列出的数据流图(DFD)或C4模型。关键是要清晰地标示出:
-
外部实体:用户、第三方系统等。
-
处理流程/组件:API网关、微服务、任务处理器等。
-
数据存储:数据库、缓存、文件系统等。
-
数据流:数据在上述元素之间是如何流动的。
-
信任边界:这是最关键的一步!用红色的虚线,圈出不同信任级别的区域。例如,从公网到API网关是一条信任边界,从一个普通业务服务到核心支付服务是另一条信任边界。跨越信任边界的数据,都需要被重点审查。
第二步:识别威胁
有了系统分解图,我们就要开始头脑风暴:“在哪个环节,可能会出什么问题?”。为了避免漫无目的地瞎想,我们可以使用一个非常经典、易于记忆的助记框架——STRIDE:
-
S - Spoofing(身份仿冒):攻击者伪装成合法的用户或服务。
-
提问:用户A如何伪装成用户B?服务X如何伪装成服务Y?
-
-
T - Tampering(数据篡改):攻击者恶意修改传输或存储中的数据。
-
提问:用户能否修改订单的价格?请求在传输过程中能否被中间人篡改?
-
-
R - Repudiation(否认):用户否认自己执行过某个操作。
- 提问:用户下单后,能否声称“这不是我操作的”?系统是否有不可抵赖的审计日志?
-
I - Information Disclosure(信息泄露):敏感信息被泄露给非授权方。
- 提问:匿名用户能否看到他人的个人信息?数据库被拖库后,会造成多大损失?日志中是否包含敏感信息?
-
D - Denial of Service(拒绝服务):攻击者通过消耗系统资源,使系统无法为正常用户提供服务。
- 提问:一个恶意的上传请求,能否耗尽服务器的磁盘空间?一个复杂的查询,能否拖垮数据库?
-
E - Elevation of Privilege(权限提升):一个低权限用户,通过漏洞,获得了高权限。
- 提问:普通用户能否通过修改请求参数,来执行管理员才能操作的功能?
第三步:评估风险
不是所有威胁都需要立即处理。我们需要对识别出的威胁进行评级,以便优先处理那些最严重的。一个简单的评级方法,就是综合评估威胁的“可能性”和“影响”。
-
高风险:可能性高,影响大。(例如:任意用户越权访问他人订单)
-
中风险:可能性或影响中等。(例如:针对后台管理系统的DoS攻击)
-
低风险:可能性低,影响小。(例如:日志中泄露了不敏感的内部配置)
第四步:设计缓解措施
针对每一个中高风险的威胁,我们需要设计具体的、可落地的缓解措施。这些措施,正是我们在第二部分“架构师的安全工具箱”中讨论的那些技术和模式。
四、 案例实战:为一个“用户个人信息模块”进行威胁建模

让我们来完整地演练一次。
业务需求:用户可以查看和修改自己的个人信息,包括昵称、手机号、收货地址。
第一步:分解系统
数据流图:
-
用户(外部实体)通过浏览器,向API网关发起请求。
-
API网关(信任边界1:公网 -> 内网)将请求转发给“用户中心服务”。
-
“用户中心服务”(处理流程)与“用户数据库”(数据存储)进行交互。
-
服务还可能调用“短信服务”(外部实体),用于修改手机号时的验证。
第二步:识别威胁(STRIDE分析)
-
S(仿冒):
-
威胁:攻击者A,通过窃取的Cookie/Token,仿冒成用户B,来查看或修改B的个人信息。
-
缓解:
-
认证:使用基于OAuth 2.0 / JWT的强认证机制,Token设置合理的有效期。
-
会话安全:在修改手机号等敏感操作时,要求进行“二次验证”(如输入密码或短信验证码)。
-
-
-
T(篡改):
-
威胁:用户A在修改自己地址的请求中,将
userId参数,从自己的ID,篡改为用户B的ID。 -
缓解:
-
授权:在“用户中心服务”的后端逻辑中,必须从当前登录的、可信的会话(Token)中获取
userId,绝不能信任任何来自客户端请求体中的userId。这是典型的水平越权漏洞。
-
-
-
I(信息泄露):
-
威胁1:一个未授权的接口,可以遍历所有用户,导致全体用户的手机号和地址泄露。
-
缓解:最小权限原则。设计
GET /api/users/me接口只返回当前登录用户的信息。任何需要列表查询的接口(如后台管理),都必须进行严格的管理员角色校验。
-
-
威胁2:数据库服务器被入侵,整个用户表被“拖库”,所有用户的明文手机号和地址泄露。
-
缓解:数据静态加密。在数据库层面,对
phone_number、address等敏感字段,进行加密存储。即使数据文件被窃取,攻击者得到的也只是一堆无法解密的密文。
-
-
威胁3:排查线上问题时,日志中打印了完整的用户信息对象,导致运维人员可以看到用户的明文手机号。
-
缓解:安全日志。在应用的代码层面,对需要打印的DTO(数据传输对象)进行处理,对敏感字段(如手机号)自动进行脱敏处理(例如,
1381234)。
-
-
-
E(权限提升):
-
威胁:用户注册时,在请求中增加了一个
"role": "admin"的字段,试图将自己注册为管理员。 -
缓解:服务端逻辑控制。在用户注册和信息修改的后端逻辑中,建立一个“字段白名单”,任何不在白名单内的字段(如
role、balance等),都应该被直接忽略或拒绝。
-
通过这样一次完整的威胁建模,我们主动地、系统性地识别出了该模块可能面临的主要安全风险,并设计了一套纵深防御的架构方案,将安全措施融入了认证、授权、数据存储、API设计的方方面面。
结语:让安全&合规成为一种本能
今天,我们从“救火队”的困境,讲到了“安全&合规左移”的理念,并学习了威胁建模这一强大的实践方法。我们必须认识到,在现代软件开发中,安全不再是一个可选项,更不是一个可以推诿给别人的“包袱”。它和功能、性能、可用性一样,是我们交付给用户的核心价值的一部分。
对于架构师而言,将安全融入设计,应当像呼吸一样自然。每一次画下一个框、一条线,我们都应该下意识地问自己:
-
这里的信任边界在哪里?
-
数据流过这里时,可能受到什么威胁?
-
我设计的认证和授权机制,是否足够健壮?
-
我是否遵循了最小权限原则?
一开始,这可能会感觉有些刻意和繁琐。但随着不断的实践,这种“安全思维”会逐渐内化,成为我们架构设计时的一种本能。当我们能够本能地、系统地规避风险,我们构建的系统,才能真正成为一座坚不可摧的、值得用户信赖的“数字堡垒”。而我们,也才能真正成长为一名对业务、对用户、对公司都高度负责的卓越架构师。
安全左移的方法论与架构实践

被折叠的 条评论
为什么被折叠?



