从开发和对抗的角度思考web网页中的接口逆向

发布于:2025-03-07 ⋅ 阅读:(15) ⋅ 点赞:(0)

如何从开发和对抗的角度去思考web网页中的接口逆向。



前言

今天突发奇想,做了一些web接口的逆向(用于信息聚合,带个人token,不做爬虫),于是在这里简单分享下,如何从开发和对抗的角度去思考web网页中的接口逆向。

  • 本文只做经验分享,不公开测试逆向的网站和逆向后的结果。
  • 本文只讲一下思路,并做一些简单测试。

1.从开发和对抗的角度思考接口逆向

开始本文前,先进行一个简单的科普,什么叫接口逆向?(针对B/S架构)

1.1 什么是接口逆向

现如今B/S架构的开发方式普遍是前后端分离,开发流程为:

  • 服务端提供能通过互联网访问的接口。
  • 前端使用Vue/React等框架进行开发,需要真实数据的时候从服务端提供的接口请求。
  • 开发完后使用webpack等工具编译JS代码。

对于一个前后端分离的网站来说,当我们随便打开一个网站,并使用F12去查看时,会发现:

  • 网络中会有真实数据的HTTP请求。
  • 前端源码一般看不懂。

这个时候,当我们去查看具体的请求,就能看到这个请求的构造,例如,我们可以查看下leetcode首页的一个接口:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

你通过这种方式就能看到接口的url、请求体、返回体,一但获得了这三个信息,你就可以自己写一个程序去调用这些接口,获取他们的数据了,当然前提是你得搞定登录和授权。

你获得了这些接口,可以干很多事情:

  • 做爬虫爬取大网站的数据。
  • 写自动化脚本去进行某些获利的操作,比如抢购、自动签到等。
  • 从接口的返回体中发现没有在页面上显示出来的信息。
  • 发现接口的水平/垂直越权漏洞。

这些事情都是对网站所有者不利的,所以它自然不想你能这么简单的拿到接口的使用方式。

它就会有一些策略来对抗你:

  • 请求接口的时候,对参数进行某种方式的签名,一旦你得签名不对,就直接请求失败。
  • 接口返回的时候,对返回的结果加密,怎么解密只有前端知道。

例如:

在这里插入图片描述

这些策略其实就是隐藏接口的调用信息,防止你干坏事。

但是,所有这些加密、签名操作都会在前端运行一遍,也就是跑在你的电脑里面,理论上你是一定能分析出它的代码逻辑的,不过在真实场景往往会有各种各样的反制手段组织你去干这件事情,干这件事情的过程,就是逆向,在这个场景里叫接口逆向。接下来,我们可以从开发者的角度来思考下,怎么做这件事情以及如何防护。

1.2 开发的角度思考如何开发策略

假设我是一个全栈开发者,现在我想将我网站的接口的关键信息隐藏起来,让别人不能这么轻易的获得,我应该怎么办?

  • 制定一套加密/签名策略,对请求的参数、返回值,甚至是url中的关键内容,前端后端同时执行这套策略。
  • 这样只有服务端接受到指定前端发送的请求才知道怎么处理,而只有指定前端才能理解服务端接口返回的内容是什么含义。

这个时候,就会涉及到一些细节问题了,我使用什么样的加密策略,什么样的签名策略?

  • 自己去实现一个吗?这些密码学相关的算法非常的严谨,自己实现的很难做到相应的安全保障,且效率方面也会比成熟的库低不少。
  • 使用开源的一些密码算法库进行组合,多次套娃。密码算法库一般涉及的运算多,如果套娃过多,特别是使用到了公钥体系中的一些算法,可能会影响接口的性能。而且如果多个算法使用到的密钥不一样还难维护。

做为一个对安全知识有限的开发者来说,最简单的办法是:

  • 制定一套简单的规则将所有参数和url拼接起来签一个名,签名的私钥写死在前端,服务端收到请求后,先使用公钥验证签名是否正确再进行后续的处理。
  • 服务端对要返回的返回值使用AES进行加密,使用到的初始向量和私钥同样写死在前端。

简单总结就是:

  • 使用成熟的密码算法库。
  • 前端写死密码算法中会使用到的密钥。
  • 不做其他的安全防护。

假设这个版本是v1。

1.3 对抗的角度思考遇到的问题

现在我是一个攻击者,攻击的目标是上述的v1,想要得到这个的具体信息,我该怎么做?

  • 对前端请求的部分打上断点,调试参数生成签名的逻辑,返回数据的处理逻辑。
  • 搜索成熟的密码算法库的特征。
  • 搜索加解密、签名、密钥等关键字。
  • 分析请求和这些特征的交互逻辑。

对于v1版本来说,上这几种手段基本就能分析出前端具体的处理逻辑了,而且大概率能看到调用某种成熟算法库进行加解密、签名的操作,这个时候,如果想使用接口,直接使用这种算法其他语言的替代库即可。

这个时候,作为开发者的我有点不开心了,因为你如此简单的就获得了我的接口,导致我的防护措施没起到作用。我也该做出反制了:

  • 调试是吧?我直接无限debugger,让你无法正常的调试。
  • 搜索密码算法库的特征是吧?我自己实现一个高效率的库,让你没办法找特征。
  • 直接搜索密钥是吧?我自己写一个生成密钥的算法,让你根本看不到明文的密钥。
  • 分析代码逻辑是吧?我直接加混淆加vm,你面对一堆乱七八糟的js代码分析去吧。

我花费了大量的成本,此时做出了v2版本,这个版本对于99%的正常开发者来说,已经很难去逆向了。对于能逆向的人来说,需要花费大量的时间去分析,就算能逆出来,也不敢大规模的使用或公开,因为一旦被发现,我再把这些策略改一改,又是需要花费许多的时间在上面了。

如果不是拿到这个接口真的能获得巨大的利益,开发者也不会花费这么多成本去防护,攻击者也不会花费这么多成本去攻击。二者的对抗中,本质上就是资源的博弈。

1.4 正常情况下开发者如何防护

  • 不必对所有的接口都采用防护策略,有些接口可以放给有需要的人调用。
  • 可以在客户端侧的关键业务做一验证码防护,例如登录、签到等业务。
  • 可以在服务侧做一些防护的策略,比如限流、特定ip高频访问重点观察。
  • 做一些动态的防护策略,例如在做某种大抢购业务时,更换前端和服务端共享的密钥等。

1.5 正常情况攻击者如何做?

二者的对抗本质是资源的博弈,如果ROI够高,也是可以考虑如下策略的。

  • 多个逆向人员分工逆向特定的部分。
  • 使用不同账户,不同的代理节点进行访问。
  • 使用机器学习算法对抗人类验证。

1.6 对抗中的胜者

既然是资源的博弈,谁花的资源少,但是收获大,才是真正的胜者。
比如:

  • 开发者在客户端随意引用很多无用的加解密流程,导致攻击者花费了大量的时间去踩坑。
  • 攻击者很简单的就发现了防护策略的弱点。

2.某个平台接口逆向案例

接上面那个对返回结果加密的例子,尝试做一次简单的逆向。

先看接口:

在这里插入图片描述

纯get请求,没有请求参数,看返回体:

在这里插入图片描述
返回体一堆字符,看其特征像base64编码,尝试解码:

在这里插入图片描述
发现是乱码,说明是字节编码而来,考虑是明文进行某种加密后再使用base64编码,调试接口的请求部分:

在这里插入图片描述

单步调试:

在这里插入图片描述

发现接口中有加密相关的布尔值,说明这个接口的内容是加密的,同时指定了parseResponse中的解密算法为i.Decrypt。全局搜索Decrypt,找到Decrypt函数的定义。

在这里插入图片描述
仔细查看代码,其实解密函数就是一个AES的CBC模式,并且IV和密钥都是明文在源码中。只需要写个简单的python脚本就能解密返回体。

from Crypto.Cipher import AES
import base64

# 将 JS 代码中 l 和 i 分别转换为 Python 对应的 key 和 iv
key = "63ca0d3f90f844928d236e132a1fee45".encode('utf-8')
iv = bytes.fromhex("00") * 16

# 假设密文为 Base64 编码的字符串,请替换为实际的加密数据
encrypted_base64 = "oUVOya5NG43FFSaV8gojbexUwU/RWNL7s/Ow***"
ciphertext = base64.b64decode(encrypted_base64)

# 创建 AES 解密器,注意这里使用 CBC 模式
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext_bytes = cipher.decrypt(ciphertext)

# 去除 PKCS7 填充
padding_length = plaintext_bytes[-1]
plaintext_bytes = plaintext_bytes[:-padding_length]

plaintext = plaintext_bytes.decode('utf-8')
print("解密后的明文:", plaintext)

结果为:

在这里插入图片描述

可以看到是一个标准的json格式,到底这个接口的防护策略就被我们突破了。


ATFWUS 2025-03-05