不只校验单个接口:基于接口调用图谱的全链路请求合法性校验方案

本系列围绕Web前端安全对抗展开。上一篇讲解了设备指纹与动态凭证绑定,解决跨设备重放攻击问题。本篇为系列第五篇,聚焦服务端侧防护:接口调用图谱的设计思路、会话状态机运行逻辑,如何拦截跳过前置流程的非法请求链路。后续第六篇将会介绍WebSocket协同与分布式熔断,做整套方案的落地复盘。

1. 前言

绝大多数Web项目的接口安全校验,都停留在单接口维度:校验Token签名、校验参数格式、校验用户权限。

但是这里存在一个很容易被忽略的安全漏洞:只校验接口本身合法,不会校验接口调用的先后顺序

攻击者在抓包拿到凭证之后,可以跳过业务前置接口,直接调用后置业务接口。举个例子:业务流程需要先登录、再获取权限、最后提交业务数据。攻击者绕过登录与获取权限步骤,直接调用提交业务的接口。

即便Token签名完全正确,这个请求在业务逻辑层面依然是非法的,单纯的单接口鉴权无法识别这类攻击。

想要解决该问题,就需要引入接口调用图谱,在服务端维护会话状态机,约束接口之间的调用依赖关系,实现全链路的请求校验。

2. 什么是接口调用图谱

接口调用图谱,是对业务系统所有API之间依赖关系的结构化描述。

图谱会记录:哪些接口必须优先执行,执行完成之后才允许调用哪些下游接口,模拟真实业务流转的先后约束。

简单示例描述:

json 复制代码
{
  "API_登录": ["API_获取权限", "API_查询基础信息"],
  "API_获取权限": ["API_业务提交"],
  "API_查询基础信息": [],
  "API_业务提交": []
}

含义:

  • 必须先调用API_登录成功之后,才可以调用API_获取权限API_查询基础信息
  • API_获取权限调用完成,才能够执行API_业务提交
  • 不允许越过登录,直接发起API_业务提交请求。

注意:接口调用图谱是在开发阶段根据真实业务流程梳理得到,不是自动生成。图谱会作为服务端会话校验的规则配置。

3. 服务端会话状态机工作机制

每一个用户独立会话,服务端都会维护一套专属的会话状态机。

  1. 会话初始化,状态机处于初始未登录状态,记录当前会话已经成功执行过的接口集合;
  2. 每收到客户端的 API 请求,首先完成签名、动态凭证、设备指纹等基础校验;
  3. 基础校验通过之后,查询接口调用图谱,判断当前接口,是否允许在会话现有状态下被调用;
  4. 如果符合图谱定义的依赖规则:放行请求,接口执行成功之后,更新状态机,把该接口标记为已完成;
  5. 如果违背调用依赖规则:直接拒绝该请求,同时标记本次会话存在风险。

#mermaid-svg-QEkyQoTRcOVY3b7h{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QEkyQoTRcOVY3b7h .error-icon{fill:#552222;}#mermaid-svg-QEkyQoTRcOVY3b7h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QEkyQoTRcOVY3b7h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QEkyQoTRcOVY3b7h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QEkyQoTRcOVY3b7h .marker.cross{stroke:#333333;}#mermaid-svg-QEkyQoTRcOVY3b7h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QEkyQoTRcOVY3b7h p{margin:0;}#mermaid-svg-QEkyQoTRcOVY3b7h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QEkyQoTRcOVY3b7h .cluster-label text{fill:#333;}#mermaid-svg-QEkyQoTRcOVY3b7h .cluster-label span{color:#333;}#mermaid-svg-QEkyQoTRcOVY3b7h .cluster-label span p{background-color:transparent;}#mermaid-svg-QEkyQoTRcOVY3b7h .label text,#mermaid-svg-QEkyQoTRcOVY3b7h span{fill:#333;color:#333;}#mermaid-svg-QEkyQoTRcOVY3b7h .node rect,#mermaid-svg-QEkyQoTRcOVY3b7h .node circle,#mermaid-svg-QEkyQoTRcOVY3b7h .node ellipse,#mermaid-svg-QEkyQoTRcOVY3b7h .node polygon,#mermaid-svg-QEkyQoTRcOVY3b7h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QEkyQoTRcOVY3b7h .rough-node .label text,#mermaid-svg-QEkyQoTRcOVY3b7h .node .label text,#mermaid-svg-QEkyQoTRcOVY3b7h .image-shape .label,#mermaid-svg-QEkyQoTRcOVY3b7h .icon-shape .label{text-anchor:middle;}#mermaid-svg-QEkyQoTRcOVY3b7h .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QEkyQoTRcOVY3b7h .rough-node .label,#mermaid-svg-QEkyQoTRcOVY3b7h .node .label,#mermaid-svg-QEkyQoTRcOVY3b7h .image-shape .label,#mermaid-svg-QEkyQoTRcOVY3b7h .icon-shape .label{text-align:center;}#mermaid-svg-QEkyQoTRcOVY3b7h .node.clickable{cursor:pointer;}#mermaid-svg-QEkyQoTRcOVY3b7h .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QEkyQoTRcOVY3b7h .arrowheadPath{fill:#333333;}#mermaid-svg-QEkyQoTRcOVY3b7h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QEkyQoTRcOVY3b7h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QEkyQoTRcOVY3b7h .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QEkyQoTRcOVY3b7h .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QEkyQoTRcOVY3b7h .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QEkyQoTRcOVY3b7h .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QEkyQoTRcOVY3b7h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QEkyQoTRcOVY3b7h .cluster text{fill:#333;}#mermaid-svg-QEkyQoTRcOVY3b7h .cluster span{color:#333;}#mermaid-svg-QEkyQoTRcOVY3b7h div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QEkyQoTRcOVY3b7h .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QEkyQoTRcOVY3b7h rect.text{fill:none;stroke-width:0;}#mermaid-svg-QEkyQoTRcOVY3b7h .icon-shape,#mermaid-svg-QEkyQoTRcOVY3b7h .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QEkyQoTRcOVY3b7h .icon-shape p,#mermaid-svg-QEkyQoTRcOVY3b7h .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QEkyQoTRcOVY3b7h .icon-shape .label rect,#mermaid-svg-QEkyQoTRcOVY3b7h .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QEkyQoTRcOVY3b7h .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QEkyQoTRcOVY3b7h .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QEkyQoTRcOVY3b7h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 符合依赖规则
违背依赖规则
客户端接口请求
基础校验:签名/凭证/设备指纹
查询接口调用图谱,校验调用顺序
执行业务接口,更新会话状态机
拒绝请求,标记会话风险

4. 敏感响应数据加密返回

仅仅拦截非法请求还不够,对于高敏感业务的接口返回数据,还需要做传输层面的保护:

  1. 会话建立完成后,服务端生成该会话独有的 AES‑GCM 加密密钥;
  2. 敏感接口返回的业务数据,全部使用会话密钥做加密,同时附带完整性认证标签;
  3. 密文数据返回前端,密钥不会暴露在普通 JS 环境,交由 WASM 模块完成解密;
  4. 如果会话被判定为风险,服务端直接废弃该会话对应的加密密钥。

这样即便网络传输数据被抓包截获,没有会话密钥也无法读取明文业务内容。

5. 完整的联合校验逻辑

实际业务中不会单独依靠接口调用图谱做判断,会和前面几篇介绍的能力做联合校验:

  • 动态凭证签名校验;
  • 设备指纹一致性校验;
  • Nonce 防重放校验;
  • 接口调用图谱 + 会话状态机校验调用顺序。

全部条件校验通过,请求才真正放行。只要其中任意一项校验失败,拒绝请求,累计异常次数。

6. 该方案收益与现实局限

收益
  • 能够拦截跳过前置业务流程的非法请求链,弥补单接口校验的短板;
  • 和设备指纹、动态凭证结合,构建多层服务端校验体系;
  • 敏感响应数据会话加密,降低抓包造成的数据泄露风险;
  • 异常请求会累计风险计数,为后续分布式熔断提供判断依据。
局限
  • 需要人工梳理全部业务接口依赖关系,业务迭代时需要同步维护更新调用图谱,存在维护成本;
  • 复杂业务场景,接口分支多,图谱配置会变得复杂;
  • 只能管控本系统定义的业务接口,无法作用于第三方依赖接口;
  • 属于防护体系的其中一环,需要配合前端 WASM、模块校验机制,实现完整的全链路防护。

7. 系列后续内容预告

  • 第六篇:长连接协同、分布式熔断机制,完整梳理整套体系的落地权衡与方案总结。

本文为系列第五篇,讲解服务端接口调用图谱与会话状态机。下一篇介绍 WebSocket 前后端协同、分布式熔断,并对整套方案做落地实践总结。

相关推荐
子兮曰14 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
云水一下14 小时前
零基础玩转bWAPP靶场(七十一):Slow HTTP DoS(慢速HTTP拒绝服务)
web安全·bwapp·slowhttptest
子兮曰14 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万15 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝15 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋15 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁16 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王952718 小时前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大18 小时前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师18 小时前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端