我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

客户端绕过风险与服务端安全防护策略:构建纵深防御体系

客户端绕过风险与服务端安全防护策略:构建纵深防御体系 1. 客户端边界防护的基础认知1.1 客户端为什么容易成为突破口做应用安全这一行久了你会发现一个绕不开的结论凡是只靠客户端做判断的业务逻辑迟早都会出问题。原因很简单客户端程序本质上是一段运行在用户设备上的代码用户对设备拥有完全控制权。这就意味着加密壳可以被扒掉调试器可以挂上去内存里的关键变量可以被直接改掉。把安全判断放在一个用户能完全操作的环境里跟把保险柜钥匙放在门口脚垫底下逻辑上并没有本质区别。我见过不少团队开发时图省事把大量校验逻辑放在前端后端只负责接收数据。项目上线初期没出事等用户量上来、有人开始用自动化工具去试探业务接口问题就集中暴露了。其实这个道理放在任何业务场景中都成立比如积分商城兑换、优惠券使用、文件上传校验、支付金额确认一旦后端不设防前端做得再花哨也只是个心理安慰。这里澄清一个边界本文讨论的是防御性视角下的客户端控制问题目的是帮助有需要的人识别人家系统里可能存在的大坑以及如何在自己的系统里把这些坑填平。正常的软件测试、安全评估应该都建立在授权和合规的前提下这也是我后面花了专门篇幅去说的一个重要内容。1.2 客户端控制通常解决哪几类问题一个典型的客户端程序承担的职责大体上可以分成几类。理解这些职责分类才能明白哪些工作适合放到客户端、哪些必须由服务端接管。第一类是交互校验比如表单里邮箱格式、手机号位数、金额是否为正数。这类校验存在的合理价值是提升用户体验用户填错了能立刻看到提示而不是把请求发到服务器才等来一个错误。但注意这类校验只是用户体验层面的辅助手段绝不是安全边界。第二类是业务规则约束比如一个活动页面规定“每个用户只能参与一次”前端代码里用本地变量记录“是否已参与”。这种设计在单机小工具里没问题但在联网业务里就非常脆弱。运行在电脑上的程序所有状态都活在内存里换一个进程、改一个返回值规则就形同虚设了。第三类是敏感数据和代码的保护包括密钥、接口地址、算法逻辑等。客户端代码可能会被打包、压缩、加壳但从安全的角度看这只能增加阅读和修改的门槛不能视为永久不可破解的保障。对抗逆向的合理目标是把破解成本抬高到远远超过破解收益而不是指望它刀枪不入。理解了这些分类就能看出所谓“绕过客户端控制”本质上是攻击者利用了“客户端环境不可信”这一事实通过修改、替换或欺诈客户端让原本由客户端代为保护的规则失效。接下来要做的就是知道问题到底出在哪些环节以及怎么去加固。2. 客户端常见漏洞盘点2.1 校验逻辑前移导致的越权风险最常见的一类问题是把接口调用权限的判断做在客户端。典型场景是这样的客户端里有一段逻辑判断当前登录用户是否有权限执行某个操作如果允许就展示按钮并允许调用接口如果不允许按钮置灰并拦截操作。看起来没什么问题但攻击者完全可以绕过客户端界面直接构造请求发往服务器。服务端如果没有再校验一次权限那就等于闭着眼睛放行。我见过一个更极端的案例某管理后台的客户端里有一组接口用来操作后台的功能模块其中部分接口甚至没有在界面里暴露。后来通过分析离线包有人发现这组接口存在于客户端代码中只要把请求里的操作对象改掉就能触发一些本不该被触发的动作。这个问题之所以严重就是因为服务端完全没有做二次校验。解决原则其实非常朴素所有关键操作必须在服务端重新完成身份校验、权限校验和业务规则校验。客户端的校验只是让正常操作更顺滑绝对不能作为唯一的安全防线。以“最小权限”为原则设计接口也是规避这类问题的重要手法——一个用户身份对应的接口范围应该是固定的而不是由客户端动态决定。2.2 敏感配置和数据留存客户端第二种高发问题是把密钥、口令、内部接口地址、加密算法参数等敏感信息写进客户端配置里。网络上有个经常被拿出来当教材的案例某公司的移动应用把第三方服务的密钥直接内置进了客户端代码结果密钥被提取后被用于调用第三方服务造成了大额损失。有人会辩解说代码做过混淆、包上加过壳别人挖不出来。但从实际安全工作的视角看混淆和加壳只能提高门槛不可能做到绝对安全。只要程序能运行密钥就必须以某种形式存在于内存中。既然存在内存里用足够的时间和工具就能还原出来。这个道理做安全的人心里要有数。更合理的做法是把敏感密钥放到服务端客户端需要调用第三方服务时由服务端代为完成签名和请求。如果出于架构原因密钥必须下发到客户端那么至少要做到定期轮换、限定权限范围并且配合远程动态下发来解决“一条密钥走天下”的问题。客户端的敏感数据能不放就不放非放不可的要有泄露后的止损预案。2.3 会话与凭证管理不当还有一个容易出问题的地方是会话管理。正常设计的会话应该具备服务端会话状态、过期时间、失效机制。但有些系统为了省事把会话状态直接保存在客户端里比如用一段可修改的数据结构里面存放“已登录是”“用户身份管理员”之类的字段。只要改掉这些字段再发给服务端就完成了简单的提权。在现代的开发框架里你更多见到的是各种凭证的存储问题。很多人习惯把登录令牌、刷新令牌一古脑儿放进本地存储或者设备内存里没有任何加密处理。一旦设备丢失、系统被植入恶意程序凭证就等于白送。即使做了一定强度的封装只要解析出真实数据依然是威胁。这个问题的防护方向有三层。第一层服务端要唯一持有会话状态客户端只保存不可篡改的凭证引用。第二层会话要有合理的有效期和失效策略长时间不操作、异地登录、异常设备指纹等状况都能触发失效。第三层数据在客户端的存储要配合系统级安全容器来使用能够加密存储并限制访问路径。3. 防护策略的核心服务端边界建设3.1 服务端校验清单客户端的控制不可靠那安全重点自然转移到服务端。服务端校验是所有防护策略里最基础也最必不可少的一环。我来梳理一份可用作参考的校验清单每一条都是实践中验证过有实际价值的身份校验确认当前请求者确实是其声称的那个人。这通常靠凭证系统解决但要注意凭证在传输过程中必须受到加密保护。权限校验确认当前请求者对这个操作、这份数据是否具备访问权。这里的颗粒度要落到具体对象上而不只是“登录用户”这个宽泛概念。数据合法性校验包括参数类型、长度、取值范围、枚举值以及业务规则。比如金额不能为负、数量不能小于零、状态不能非法跳变。完整性校验防止请求在传输过程中被篡改。常见的做法是通过签名机制把请求内容固定下来任何改动都会导致签名不匹配。幂等性校验对重复提交、重放攻击做防御。尤其涉及支付、积分变动这类会影响业务状态的接口应确保同一请求只生效一次。这份清单写出来容易真正落地时最容易被忽略的是“对每个接口都执行完整校验链路”而不是只做其中一部分。比如权限校验只做了登录状态判断没做对象归属判断依然可能产生越权数据读取。再比如所有参数都校验了但没有加入签名攻击者就能截获请求后篡改参数重新发出。所以服务端校验的关键词是“完整”和“一致”。一套严格的校验方法必须同时覆盖上述几个维度且对所有外部接口保持一致不能出现“这个接口校验了那个接口没校验”的漏洞。3.2 会话与认证保护会话保护是一个经常被低估的环节。理解会话安全可以先了解一下会话的典型生命周期用户登录后服务端建立会话返回一个凭证后续请求带着凭证来服务端凭凭证识别身份当用户退出或会话过期后凭证失效。在这个生命周期里常见的安全薄弱点首选的还是凭证泄露风险。凭证如果放在URL参数里会残留到历史记录和应用日志中凭证如果放在本地无保护存储里可能被其他应用读取凭证如果长期不过期就大幅扩大了泄露后的攻击窗口。所以凭证存放位置、传输通道、有效期三者都值得注意。另一个薄弱点是会话固定攻击攻击者可以诱导用户使用某个预先设定的会话标识登录登录后攻击者复用同一个会话标识从而接管用户会话。对付会话固定攻击核心手段是在登录完成后强制更换会话标识并将新旧会话完全剥离。这个看似简单的变更能化解一个很危险的攻击面。再有一个实践上很多人会忽略的问题会话注销。不少系统的“退出登录”只删了客户端的缓存服务端会话仍然活着。这样做用户点击退出后令牌依然有效一旦令牌泄露依然能够被继续使用。正确做法是退出时请求服务端销毁会话同时清理客户端本地所有会话相关数据。3.3 数据加密与脱敏关于数据安全需要区分两类目标一类是防止数据被窃听后直接可读另一类是防止数据被拿到后引发进一步风险。对应到实践中就是传输加密和存储脱敏两个动作。传输侧全链路加密是最基础的要求。客户端和服务端之间的通信应始终使用可靠的加密协议避免数据以明文形式在传输过程中暴露。这里值得强调“全链路”不只是客户端到服务端的第一跳服务端内部以及与第三方系统之间的数据流转同样需要纳入加密范围。存储侧密钥、凭证这类高敏感数据要加密存储这一点大家都容易接受。容易被忽略的是业务数据的脱敏规则。比如客户端上要显示一个订单列表里面包含收货人姓名、手机号、详细地址这些信息对客户端完成展示来说是必要的但对一个只做页面渲染的组件来说是否一定要下发完整的明文数据其实值得打个问号。采用“按需最小化”的数据下发策略只下发当前功能必须的数据配合展示层的脱敏规则即使数据被截获或本地被读取风险也能得到控制。上面这些策略核心是在服务端形成完整的防御闭环而不是指望客户端单方面自觉。4. 客户端程序自防护与检测思路4.1 防篡改与自校验机制服务端做得好是不是客户端就可以完全不管了也不是。虽然客户端控制不能作为安全边界但适当的客户端自防护可以抬高攻击成本让攻击者在绕过时必须付出更多时间与精力从而降低系统被批量攻击的概率。防篡改机制是一种可选的做法。原理是让客户端在启动时或运行中计算自身关键文件、关键代码段的哈希并与内置或服务端下发的期望值进行比对。一旦发现文件被改动、代码被注入就拒绝继续运行或进入受限模式。实现上需要注意期望值本身如果存放在客户端中攻击者可以通过修改本地期望值来绕过校验因此更可靠的做法是把比对逻辑和服务端策略结合由服务端周期下发改动规则。自校验机制的另一个实用价值是应对调试器附加等动态分析行为。检测到调试特征时可以主动变更执行路径或减慢运行速度干扰动态分析过程。这个思路能起到的作用和防篡改类似都是增加攻击成本而不是绝对阻止。做客户端自防护时有一个需要明确的心理预期它并不能让客户端变得不可破解它只是把破解变成一个需要更多时间、更多工具、更专业能力才能完成的工程。对大多数依赖通用攻击脚本的自动化攻击来说这个门槛已经足够劝阻一大部分了。4.2 异常行为监控与实时阻断自防护机制更多作用于客户端自身而异常行为监控则是从服务端视角去识别客户端行为中出现的异常信号。这两者配合可以形成一个比较完整的防御体系。服务端可以从几个维度进行监控。首先是请求特征的异常同一个客户端在短时间内发出大量结构相似的请求请求参数中出现不符合业务逻辑的值请求速度远超正常人类的操作频率等这些信号都值得关注。其次是客户端环境特征的异常同一个账号短时间内从多个不合理的设备或网络位置登录使用的客户端版本异常老旧或频繁变动等。监控发现异常后处理方式可以分阶梯进行低频异常先告警记录日志并观察趋势持续异常则进入更严格的验证流程比如要求二次认证、临时限制操作范围确认有攻击行为的直接阻断并触发会话失效。这里有一个要点监控规则要基于业务基线来设定而不是随便定个阈值。不同业务场景的正常曲线差异巨大一个学习平台的日间活跃高峰和一个支付工具的深夜交易高峰明显不属于同一种模型。没有业务基线的规则很容易产生大量误报导致监控人员慢慢失去对告警的敏感性。5. 如何规范评估客户端控制5.1 授权范围与专业测试流程前面几章讲的都是加固思路这一章专门说说如果你是一名安全测试人员接到客户端控制相关的评估任务应该怎么做。这里有一个极其重要的前提需要明确任何针对目标系统的安全测试都必须事先获得书面授权并明确测试范围、测试时间、允许测试的资产边界。脱离授权前提去做测试无论测试目的是什么都可能构成违规行为。规范化的客户端控制评估流程一般包括下面几个阶段。第一步是信息收集阶段了解目标客户端的技术栈、框架结构、通信协议、部署方式。这一步不需要实际攻击行为只是通过合法的途径做静态了解。第二步是分析阶段基于收集到的信息梳理客户端存在的潜在薄弱点比如是否存放了敏感配置、是否存在关键的校验逻辑、凭证管理方式是否有风险。第三步是验证阶段在授权范围内针对分析出来的薄弱点进行可控的实际验证证明风险是否真实存在。整个流程中要特别强调“可控”。验证动作应该精心设计在最大限度还原风险场景的同时避免对目标业务产生超出预期的破坏。比如验证某项操作是否存在越权时优先使用自己名下的测试账号来构造请求而不是直接拿别人的真实账号做实验。5.2 风险定级与修复优先级评估的最终产物是一份高质量的风险报告。光列出“发现了哪些漏洞”是不够的还要把漏洞的利用门槛、影响范围、潜在损失讲清楚这样才能帮助业务方规划修复优先级。风险定级一般可以参考两个维度一个是利用难度换句话说就是攻击者构造一次成功攻击需要什么样的技术能力、需要多少前置条件另一个是影响程度一旦被利用会造成数据泄露、资金损失还是服务中断。两者结合后高利用难度高影响、低利用难度高影响这两类应该是修复优先级最高的对象。修复优先级还要考虑修复成本。有些问题比如“服务端缺少幂等性校验”修复起来改动范围很小却收益很大没有理由拖延。有些问题比如“客户端做了自定义加密方案”修复起来涉及大面积改动需要定一个渐进式迁移的节奏。在报告里把这些问题区分开才能帮助业务方做有效取舍。我在实际报告工作中还习惯给每个问题附上具体的复现步骤、风险证据和修复建议摘要。这样开发团队拿到报告不需要在自己猜怎么复现直接照着就能定位到代码位置整个修复周期会明显缩短。6. 实际工作中的经验最后分享几个我在实际项目中积累的体会都是踩过坑之后才慢慢领悟的。第一点安全设计一定要前置到架构阶段而不是等代码写完了再来补。我记得有一个项目做到开发中期才被测试发现大量的业务规则都堆在客户端校验。当时如果要迁移到服务端涉及几十个接口的改造进度压力巨大。后来只能加班加点分批迁移折腾了好几个迭代才消化掉。如果架构设计阶段就想清楚“客户端只负责体验、服务端负责安全”这个原则整个过程会省太多事。第二点防客户端绕过的方案要分层构建不要迷信单一措施。有些团队花大力气在客户端做了高强度混淆觉得自己高枕无忧结果服务端的接口一点校验都没做攻击者根本不需要分析客户端逻辑直接抓包改包就打通了一切。这是舍本逐末。正确的姿势是用客户端自防护抬高门槛用服务端校验守住底线用监控感知异常三者各司其职。第三点对“客户端被绕过”要有一个从容的心态。任何客户端程序面对本地环境都是透明的这跟用户是不是恶意没有关系。设计目标应该是系统在客户端被完全控制时依然不会因为这种控制而产生超出预期的安全后果。想通这一点很多设计决策都会清晰很多。第四点安全评估和防护建设是一个循环的过程。系统上线不是终点随着业务迭代、框架升级、新的攻击技巧出现旧的安全边界可能慢慢失效。定期做复评、持续监控告警、及时更新防护策略比一次性建设要难做得多但这才是有长期价值的安全工作。如果你只是需要一份“看起来安全”的报告那这套体系确实不适合你如果想让系统真正扛得住试探就把它当成一个持续运营的工程来做。
返回列表