逆向工程入门 - 社交平台移动端发微博 API 分析

逆向工程入门 - 社交平台移动端发微博 API 分析

免责声明: 本博客内容仅供学习和研究使用,请勿用于非法用途。任何未经授权的逆向工程行为都可能违反相关法律法规和服务条款,风险自负。

前言: 继上一篇博客详述了使用 JS 注入绕过 msedgedriver 的字符处理逻辑并触发表单提交元素后, 为更深入地理解某知名社交平台的前端逻辑, 开始尝试使用浏览器的开发者工具, 以更深入地理解 Web 开发的底层原理.

即使在没有 JS 基础语法知识的情况下, 有一些更简单的方法也能帮助新手快速入门逆向工程.

逆向工程的准备工作

通过分析网络请求来尝试理解该平台 API 的工作原理.

使用浏览器开发者工具的“网络”(Network)面板:

  • 步骤:

    1. 打开该平台 (域名: m.xxx.cn) 的发微博页面。
    2. F12 打开开发者工具,切换到 Network 面板。
    3. 勾选 Preserve log 选项,这样在页面跳转后,之前的网络请求记录不会被清除。
    4. 在该平台页面输入一些内容,并点击发送按钮。
    5. Network 面板中,查看发送微博的网络请求(通常是一个 POST 请求,可以通过 Method 列来筛选)。
  • 你能看到什么:

    • 请求的 URL: 发送微博的接口地址。
    • 请求的方法: 例如 POST
    • 请求头(Request Headers): 包括 Content-TypeCookie 等信息。
    • 请求体(Request Payload): 发送给服务器的数据,通常包括你输入的微博内容、以及其他一些参数(例如时间戳、token 等)。
    • 响应头(Response Headers): 服务器返回的头信息。
    • 响应体(Response): 服务器返回的数据,通常是一个 JSON 格式的字符串,包含了一些状态信息(例如是否发送成功、错误代码等)。
  • 如何利用这些信息:

    • 通过分析发送微博的网络请求,可以了解该平台客户端和服务器之间是如何交互的。
    • 可以知道发送微博需要哪些参数,以及服务器是如何响应的。
    • 可以通过查看请求体来了解微博内容是如何被编码和传输的。
问题一:发送微博后跳转到新标签页,导致 Network 记录丢失

解决方法:

  1. 启用 “Preserve log” 选项:

    • Network 面板的工具栏中,确保勾选 Preserve log(或 保留日志)选项。这个选项可以让 Network 面板在页面跳转后仍然保留之前的网络请求记录。
    • 勾选此选项后,再次尝试发送微博,你会发现之前的网络请求记录都被保留了下来。
  2. 使用 “Disable cache” 选项:

    • Network 面板的工具栏中,勾选 Disable cache(或 禁用缓存)选项。这个选项可以确保你每次请求的都是最新的资源,而不是缓存中的旧资源。这对于调试网络请求非常有用。
问题二:如何过滤出 POST 请求

解决方法:

  1. 使用过滤器进行筛选:

    • Network 面板的工具栏中,有一个 Filter(或 过滤)输入框。你可以在这里输入一些关键词来过滤网络请求。
    • 输入 method:POST,然后按下回车键,Network 面板将只显示 POST 请求。
  2. 根据请求的 URL 进行筛选:

    • 该平台发送微博的接口地址名称是 update。在 Filter 输入框中输入这个关键词来过滤网络请求。
分析找到的信息:

第一张图:负载(Payload)

  • content “请求体种包含了这条信息!!!”,这里就是发送的微博内容。
  • visible 此处的测试我是通过发送了这条"仅自己可见" 的微博. 所以此处的 1,表示发送的微博的可见范围是"仅自己可见"。据此, 可以猜测, 0 可以表示“所有人可见”,6 可能表示“仅好友圈可见”。我们可以通过修改这个参数来测试不同的可见范围对应的数字。
  • st f79388,看起来像是一个 token,可能是用于验证用户身份或防止重复提交的。
  • _spr screen:1920x1080,表示当前的屏幕分辨率。

第二三张图:标头(Headers)

  • 请求网址(Request URL): https://m.xxx.cn/api/statuses/update,表明发送微博的 API 接口地址是 /api/statuses/update
  • 请求方法(Request Method): POST,表明发送微博是通过 POST 请求来完成的。
  • 状态码(Status Code): 200 OK,表明请求已成功处理。
  • 远程地址(Remote Address): 127.0.0.1:7890,表示我使用了代理服务器, 后文将会详细分析这点。
  • 请求标头(Request Headers):
    • Accept application/json, text/plain, */*,表示客户端可以接受 JSON、纯文本等多种类型的响应。
    • Accept-Encoding gzip, deflate, br, zstd,表示客户端支持的压缩算法。
    • Accept-Language zh-CN,zh;q=0.9,en;q=0.8,表示客户端接受的语言。
    • Content-Length 193,表示请求体的长度。
    • Content-Type application/x-www-form-urlencoded,表示请求体是使用 URL 编码的表单数据。
    • Cookie 一长串的 经过处理的 Cookie 值,用于用户身份验证。
    • Origin https://m.xxx.cn,表示请求的来源。
    • Referer https://m.xxx.cn/compose/,表示用户是从哪个页面跳转过来的。
    • User-Agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36,表示你使用的浏览器信息。
    • X-Requested-With XMLHttpRequest,表示这是一个 AJAX 请求。
    • X-XSRF-TOKEN f79388,看起来和负载中的 st 值相同,可能是一个 CSRF token,用于防止跨站请求伪造攻击。

我们可以分析出什么?

  1. 微博发送 API 的基本信息: 我们知道了发送微博的 API 接口地址是 https://m.xxx.cn/api/statuses/update,请求方法是 POST,请求体是使用 URL 编码的表单数据。
  2. 微博内容参数: 微博内容是通过 content 参数来传递的。
  3. 可见性参数: 通过 visible 参数来控制微博的可见范围。
  4. 身份验证: 该平台使用 CookieX-XSRF-TOKEN 来进行用户身份验证和防止 CSRF 攻击。
  5. 客户端信息: 该平台会收集用户的屏幕分辨率 (_spr) 和浏览器信息 (User-Agent)。

接下来可以做什么?

  1. 修改参数: 尝试修改请求体中的参数,例如 contentvisible,看看会对发送结果产生什么影响。
  2. 构造请求: 尝试使用 Python 的 requests 库来构造和发送 POST 请求,模拟发送微博的过程。
  3. 分析响应: 仔细分析服务器返回的响应数据,看看能不能发现一些有用的信息,例如错误代码、状态信息等。
  4. 深入研究: 如果想更深入地了解该平台 API,可以尝试使用代理工具拦截和修改网络请求,或者阅读公开的 API 文档和相关的技术博客。

关于远程地址(Remote Address): 127.0.0.1:7890 以及相关的 ip 概念确认

上面我的服务器请求中的远程地址, 确实是我使用的代理地址. 我使用的是 Clash 的“规则”模式而不是全局, 按理说, 会根据规则列表来判断哪些流量走代理,哪些流量直连。如果规则设置正确,访问国内网站时应该不会走代理. 那这里为什么走了代理?

可能原因:

  1. 规则不完善: Clash 规则列表中可能没有包含所有国内网站的域名或 IP 地址,导致一些国内网站的流量被误判为需要走代理。
  2. DNS 污染: 即使规则设置正确,也可能因为 DNS 污染导致域名被解析到了错误的 IP 地址,从而导致流量被路由到了代理服务器。
  3. Clash 的 Bug: Clash 本身也可能存在一些 bug,导致规则无法正确执行。

求证方案:

  • 检查 Clash 规则列表: 确保规则列表中包含了所有国内网站的域名和 IP 地址。参考公开的规则列表,例如 https://github.com/Loyalsoldier/clash-rules
  • 使用自定义 DNS: 在 Clash 中设置一个可靠的 DNS 服务器,例如 114.114.114.1148.8.8.8,以避免 DNS 污染。
  • 更新 Clash: 确保你使用的是最新版本的 Clash,并关注 Clash 的更新日志,看看是否有相关的 bug 修复。

解惑

我在 https://github.com/Loyalsoldier/clash-rules 查了 Clash 直连域名列表 direct.txt, 确定了没有 m.xxx.cn (连 xxx.cn 都没有). 现在可以确定确实使用了代理服务器. 127.0.0.1 是本地回环地址,用于指代本机。之所以显示这个地址,是因为 Clash 在我的本机上启动了一个代理服务,并监听 7890 端口。 当我的浏览器或其他应用程序通过 Clash 代理访问网络时,它们的请求会先发送到 Clash 的代理服务(也就是 127.0.0.1:7890),然后由 Clash 根据规则决定是将请求直连还是转发到代理服务器。

所以,即使访问的 m.xxx.cn 是国内网站,如果浏览器设置了使用 Clash 代理,那么远程地址仍然会显示为 127.0.0.1:7890。这并不意味着请求没有直连,而是因为 Clash 在中间做了一层转发。

请求实际上是:

我的应用程序 -> Clash 代理服务 (127.0.0.1:7890) -> (根据规则) -> 代理服务器 或 直连目标网站

根据我的终端 ipconfig 可以知道, 我的 ipv4 地址是 172.xx.xx.x,而这个地址 是局域网 IP(私有 IP)。

  • 公网 IP 是指在全球互联网中唯一的 IP 地址,可以直接从互联网访问。
  • 局域网 IP 是指在局域网内部使用的 IP 地址,不能直接从互联网访问。

电脑通过路由器连接到互联网,路由器会分配一个局域网 IP 给电脑,例如 172.xx.xx.x。路由器还有一个公网 IP,用于与外部网络通信。

关于 192.168.2.1 这个以太网 ip, 很可能是路由器的 IP 地址,而不是 ISP(互联网服务提供商)的地址。

192.168.x.x 是一个常用的局域网 IP 地址段,很多路由器默认使用这个网段。网上很多人的以太网 IP 都是 192.168.x.x,是因为他们都使用了路由器,并且路由器使用了默认的 IP 地址段。

**开门! **

关于 https://m.xxx.cn/api/statuses/update 的访问结果:

尝试直接访问 https://m.xxx.cn/api/statuses/update 得到以下结果:

{
  "ok": 0,
  "errno": "100007",
  "msg": "不符合的请求方式",
  "extra": ""
}

这是因为使用了 GET 方法访问了这个 API 接口,而它只接受 POST 请求。"不符合的请求方式" 指的就是这个意思。这也进一步佐证了发送微博的请求方式是 POST.

关于 visiblest 参数:

由于刚才我的一条微博设置了仅自己可见, 我们可以确定 visible: 1 表示“仅自己可见”. 观察更早的几分钟前发的几条微博的数据, 我发现, 这一段时间请求体中不同的微博内容, 有相同的 st: f79388,接下来我新发送了 2 条微博, 一条公开, 发现请求体中更新为 st: f13778,visible 这一行消失; 一条朋友圈可见, st: f13778,visible: 6. 不同时间段的 st 值会发生变化。这表明 st 很可能是一个 与时间相关的 token。它可能用于标识某个时间段内的请求,或者用于防止 CSRF 攻击。

可以知道:

  • visible: 0 (或不存在): 公开
  • visible: 1 : 仅自己可见
  • visible: 6 : 朋友圈可见

可以尝试发送更多不同可见性的微博来验证这个推断

使用 requests 构造请求:

刚才我们已经抓包得到了请求头中的 User-Agent 字段, 所以接下来我们可以使用 Python 的 requests 库来构造一个 POST 请求,并设置请求头,来模拟发送微博的过程。

为什么要构造请求头?

  • 伪装成浏览器: 服务器可以通过请求头中的 User-Agent 字段来识别客户端的类型。如果我们想让服务器认为我们的请求来自一个正常的浏览器,就需要设置一个正确的 User-Agent
  • 通过服务器验证: 一些服务器会检查请求头中的某些字段,例如 RefererOrigin 等,来判断请求是否合法。我们需要根据实际情况设置这些字段,才能通过服务器的验证。
  • 传递更多信息: 请求头中还可以包含其他一些信息,例如 CookieAccept-Encoding 等,这些信息可以告诉服务器更多关于客户端的信息。

为什么之前的脚本(如 GhostwriterWeibo_v2.py )没有构造请求头?

之前的脚本使用了 Selenium,Selenium 会自动控制浏览器发送请求,所以它会自动设置请求头,我们不需要手动设置。这也是这几行关于设置请求头的代码被注释掉的原因,因为对于 Selenium 来说,手动设置请求头是没有意义的.

# 设置请求头, 会被selenium忽略, 没有意义
    # if headers:
    #     edge_options.add_argument(f'--user-agent={headers["User-Agent"]}')

代码示例:

下面是一个使用 requests 库构造请求并发送微博的示例代码:

import requests

def send_weibo(content, visible=0, cookie=None, st=None):
    """使用 requests 库发送微博

    Args:
        content (string): 微博内容
        visible (int, optional): 可见性. 0: 公开, 1: 仅自己可见, 6: 朋友圈可见. Defaults to 0.
        cookie (string, optional): 微博 Cookie. Defaults to None.
        st (string, optional): 用于验证的 token, 需要抓包获取. Defaults to None.
    """

    url = "https://m.xxx.cn/api/statuses/update" 

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
        "Referer": "https://m.xxx.cn/compose/",
        "Origin": "https://m.xxx.cn", 
        "Content-Type": "application/x-www-form-urlencoded",
        "X-Requested-With": "XMLHttpRequest",
        "X-XSRF-TOKEN": st,  # 使用抓包获取到的 st 值
        "Cookie": cookie,  # 使用你的经过处理的 Cookie 
    }
    if visible == 0:
        data = {
            "content": content,
            "st": st,
            "_spr": "screen:1920x1080" # 根据屏幕分辨率修改
        }
    else:
        data = {
            "content": content,
            "visible": visible,
            "st": st,
            "_spr": "screen:1920x1080" # 根据屏幕分辨率修改
        }

    response = requests.post(url, headers=headers, data=data)

    if response.status_code == 200:
        result = response.json()
        if result["ok"] == 1:
            print("微博发送成功!")
        else:
            print(f"微博发送失败: {result['msg']}")
    else:
        print(f"请求失败: {response.status_code}")

# 使用示例
# 从浏览器开发者工具中复制 Cookie 和 st 的值
my_cookie = "经过处理的Cookie"  # 替换成你的经过处理的 Cookie 
my_st = "f13778"  # 替换成抓包获取到的 st 值

send_weibo("发送一条公开微博!", cookie=my_cookie, st=my_st)  # 发送一条公开微博
send_weibo("测试使用 requests 发微博, 仅自己可见!", visible=1, cookie=my_cookie, st=my_st)  # 发送一条仅自己可见的微博
send_weibo("一条朋友圈可见的微博", visible=6, cookie=my_cookie, st=my_st)  # 发送一条朋友圈可见的微博

代码说明:

  1. send_weibo 函数:
    • content:微博内容。
    • visible:可见性,默认为 0(公开)。
    • cookie:微博 经过处理的 Cookie。需要从浏览器开发者工具中复制 Cookie,并粘贴到这里。
    • st:用于验证的 token。需要从抓包获取的 X-XSRF-TOKEN 中复制 st 的值,并粘贴到这里。
  2. url 微博发送 API 的地址。
  3. headers 请求头。需要根据抓包获取的信息来设置 User-AgentRefererOriginContent-TypeX-Requested-WithX-XSRF-TOKENCookie
  4. data 请求体。需要根据 visible 的值来构造请求体。
  5. requests.post 发送 POST 请求。
  6. response.json() 将响应数据解析为 JSON 格式。
  7. 判断发送结果: 根据响应数据中的 ok 字段来判断微博是否发送成功。

如何获取 Cookiest

  1. 打开该平台 (域名: m.xxx.cn) 的发微博页面。
  2. F12 打开开发者工具,切换到 Network 面板。
  3. 勾选 Preserve logDisable cache 选项。
  4. 在该平台页面输入一些内容,并点击发送按钮。
  5. Network 面板中,找到发送微博的 POST 请求。
  6. 在请求的 Headers 选项卡中,找到 Request Headers 部分。
  7. 复制 Cookie一部分.
  8. 复制 X-XSRF-TOKEN 的值(就是 st 的值)。

重要提示:

  • Cookie 和 st 的有效期: Cookiest 都有有效期,过期后需要重新获取。具体时效将在后文继续探讨.
  • 频率限制: 频繁发送微博可能会触发该平台的反爬虫机制,导致账号被限制。请合理使用此代码,不要滥用。
  • 免责声明: 这段代码仅供学习和研究使用,请勿用于非法用途。

现在, 尝试修改代码中的参数,例如微博内容、可见性、Cookiest, 使用抓包获取的值, 看看会对发送结果产生什么影响.

我尝试发送了 3 条不同可见性的微博, 可以验证 0 公开, 1 仅自己可见, 6 朋友圈可见的推断.

关于 st 值:

st 值可能会在一段时间内保持不变。抱着侥幸心理, 我先尝试了使用之前的 f13778,看看能不能发送成功。

  • 如果发送成功: 说明 st 值在测试的时间段内仍然有效。
  • 如果发送失败: 很可能是 st 值已经过期,或者该平台更新了安全策略。需要重新抓包获取新的 st 值。

请注意: st 值很可能与时间有关,或者与登录状态有关。如果隔了很长时间再次运行代码,或者该平台账号退出了登录,很可能需要重新获取 st 值。

关于抓包:

以上研究网络请求并抓取数据的过程,就是抓包

抓包(Packet Capture)是指捕获网络上传输的数据包的过程。我们通常使用一些工具来辅助抓包,例如浏览器开发者工具、Wireshark、Fiddler、Charles 等。

  • 使用浏览器开发者工具的 Network 面板来查看网络请求,就是一种非常常用的抓包方式。 它可以方便地查看 HTTP 请求和响应的详细信息,包括请求头、请求体、响应头、响应体等。
  • Wireshark 是一款更专业的网络抓包工具,它可以捕获所有经过网卡的网络数据包,不仅仅是 HTTP 协议。 Wireshark 功能更强大,但也更复杂。它可以捕获更底层的数据包,例如 TCP、UDP、IP 等。

Wireshark vs. 浏览器开发者工具:

特性 浏览器开发者工具 Wireshark
捕获范围 仅限于当前浏览器的 HTTP/HTTPS 请求 可以捕获所有经过网卡的网络数据包
使用难度 相对简单,适合 Web 开发调试 更复杂,需要一定的网络协议知识
功能 主要用于 Web 开发调试,查看 HTTP 请求和响应的详细信息 功能更强大,可以进行更深入的网络分析
实时性 实时显示网络请求 可以实时捕获数据包,也可以保存数据包供以后分析
协议支持 主要支持 HTTP/HTTPS 支持多种网络协议,例如 TCP、UDP、IP、ARP、ICMP 等
操作系统支持 跟随浏览器 跨平台,支持 Windows、macOS、Linux 等

小结:

  • 对于 Web 开发调试来说,浏览器开发者工具已经足够用了。 它可以让你方便地查看 HTTP 请求和响应的详细信息,而且使用起来也比较简单。
  • 如果需要进行更深入的网络分析,或者需要捕获非 HTTP 协议的数据包,那么可以使用 Wireshark。 Wireshark 可以说是更“底层”一些,因为它可以捕获更底层的网络数据包。

开始执行脚本

当我使用刚才的 st, 终端显示:

请求失败: 403
请求失败: 403
请求失败: 403

代码 403, 被服务器拒绝了, 所以我重新手动发送微博, 发现 st 已经更新, 而且 cookie 也跟着更新, 我的 cookie

_T_WM=...; XSRF-TOKEN=f13778

变成了

_T_WM=...; XSRF-TOKEN=8ea381; mweibo_short_token=bf3d747a9d

st 也藏在 cookie 中. 我怀疑 m.xxx.cn 比多数人使用的网页版该平台 xxx.comcookie 更新更快, 基本上是几十分钟就更新(实际上这时候的说法并不准确, 请看下文继续分析).

更新 cookiest 之后, 发送成功:

微博发送成功!
微博发送失败: 发微博太多啦,休息一会儿吧!
微博发送失败: 发微博太多啦,休息一会儿吧!

关于我的发现:

  1. st 值和 Cookie 的更新: st 值和 Cookie 几乎是同时更新的,这表明它们之间存在很强的关联性。st 值很可能是基于 Cookie 生成的,用于验证用户的登录状态和防止 CSRF 攻击。
  2. st 值藏在 Cookie 中: XSRF-TOKEN 的值和 Cookie 中的某个字段值相同,这进一步证实了 st 值和 Cookie 之间的关联性。这很可能是该平台的一种安全机制,用于验证请求的合法性。
  3. m.xxx.cn 的 Cookie 更新频率: 怀疑 m.xxx.cnCookie 更新频率比 xxx.com 更快,这很可能是因为移动端网站更注重安全性,所以会更频繁地更新 Cookie

关于发送结果:

  • 第一条微博发送成功,说明更新后的 Cookiest 值是有效的。
  • 第二条和第三条微博发送失败,提示“发微博太多啦,休息一会儿吧!”,说明触发了该平台的频率限制。

计划:

  • 为了避免触发该平台的频率限制,可以在每次发送微博之间添加一些延时,例如 time.sleep(60),让程序暂停 60 秒。
  • 继续探索 m.xxx.cnxxx.comCookie 更新机制,看看能不能发现更多有趣的规律。
  • 关于 XSRF-TOKENSUB XSRF-TOKEN 的值和 CookieSUB 的一部分相同, 这是该平台的一种安全策略。SUB 是一个经过 Base64 编码的字符串,里面包含了用户的一些信息,以及一个用于验证请求合法性的 token。XSRF-TOKEN 的值通常是 SUB 中 token 部分的哈希值。

代码优化:

为了避免频繁更新 Cookiest,将它们保存到文件中,并在每次发送微博之前检查它们是否过期。如果过期了,再重新获取。

另外, 把 XSRF-TOKEN 单独定义出来, 这样更方便:

import requests
import time
import json

def send_weibo(content, visible=0, cookie=None, x_xsrf_token=None):
    """使用 requests 库发送微博

    Args:
        content (string): 微博内容
        visible (int, optional): 可见性. 0: 公开, 1: 仅自己可见, 6: 朋友圈可见. Defaults to 0.
        cookie (string, optional): 微博 Cookie. Defaults to None.
        x_xsrf_token (string, optional): 用于验证的 token, 需要抓包获取. Defaults to None.
    """

    url = "https://m.xxx.cn/api/statuses/update" 

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
        "Referer": "https://m.xxx.cn/compose/", 
        "Origin": "https://m.xxx.cn", 
        "Content-Type": "application/x-www-form-urlencoded",
        "X-Requested-With": "XMLHttpRequest",
        "X-XSRF-TOKEN": x_xsrf_token,
        "Cookie": cookie,
    }
    if visible == 0:
        data = {
            "content": content,
            "st": x_xsrf_token,
            "_spr": "screen:1920x1080" # 根据屏幕分辨率修改
        }
    else:
        data = {
            "content": content,
            "visible": visible,
            "st": x_xsrf_token,
            "_spr": "screen:1920x1080" # 根据屏幕分辨率修改
        }

    response = requests.post(url, headers=headers, data=data)

    if response.status_code == 200:
        result = response.json()
        if result["ok"] == 1:
            print("微博发送成功!")
            return True
        else:
            print(f"微博发送失败: {result['msg']}")
            return False
    else:
        print(f"请求失败: {response.status_code}")
        return False

# 使用示例
# 从浏览器开发者工具中复制 Cookie 和 X-XSRF-TOKEN 的值
my_cookie = "修改成你的经过处理的Cookie"  # 替换成你的经过处理的 Cookie 
my_x_xsrf_token = "8ea381"  # 替换成抓包获取到的 X-XSRF-TOKEN 值

# 首次发送
send_weibo("发送一条公开微博!", cookie=my_cookie, x_xsrf_token=my_x_xsrf_token)

# 增加延时
time.sleep(60)

# 再次发送
send_weibo("测试使用 requests 发微博, 仅自己可见!", visible=1, cookie=my_cookie, x_xsrf_token=my_x_xsrf_token)

# 增加延时
time.sleep(60)

# 再次发送
send_weibo("一条朋友圈可见的微博", visible=6, cookie=my_cookie, x_xsrf_token=my_x_xsrf_token)

接下来的探索方向:

  1. 研究 Cookie 的生成机制: 尝试分析 Cookie 中各个字段的含义,看看能不能找到生成 Cookie 的算法。
  2. 研究 st 值的生成机制: 尝试找出 st 值与时间、Cookie 和其他参数之间的关系。
  3. 探索该平台 API 的更多接口: 除了发送微博的接口,该平台还有很多其他的 API 接口,例如获取用户信息、获取评论列表、点赞等。可以尝试使用类似的方法来探索这些接口。

关于 CSRF 攻击:

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的 Web 安全漏洞,它允许攻击者诱导用户执行他们不打算执行的操作。

攻击原理:

  1. 诱导: 攻击者诱使用户访问一个恶意网站或点击一个恶意链接。
  2. 伪造请求: 恶意网站会向用户已登录的目标网站(例如该平台)发送一个伪造的请求。
  3. 利用 Cookie: 由于浏览器会自动将目标网站的 Cookie 附加到请求中,因此伪造的请求看起来就像是由用户自己发送的一样。
  4. 执行操作: 目标网站收到伪造的请求后,会认为是用户自己发出的,并执行相应的操作,例如发布微博、修改密码、转账等。

举个例子:

假设你已经登录了该平台,攻击者诱使你访问了一个恶意网站。恶意网站中包含以下代码:

<img src="https://xxx.com/api/statuses/update?content=我是被攻击者发布的微博&visible=0" width="0" height="0">

在这个例子中, 攻击者将恶意 URL 嵌入到了 HTML 的 标签的 src 属性中. 而且 不一定需要点击图片。 只要浏览器加载了这个 标签,就会自动向 src 属性指定的 URL 发送请求,无论图片是否可见或是否被点击。在这个例子中,width="0" height="0" 使得图片在页面上不可见,所以用户甚至察觉不到这个请求的发生。当你访问这个恶意网站时,浏览器会自动向 https://xxx.com/api/statuses/update 发送一个 GET 请求,并附带上你的该平台 Cookie。该平台服务器收到请求后,会认为是你自己发布的微博,并执行相应的操作。

针对m.xxx.cn, 之前我们已经得出发送微博的 API 接口地址是 https://m.xxx.cn/api/statuses/update, 所以应该为https://m.xxx.cn/api/statuses/update?content=我是被攻击者发布的微博&visible=0, 这里原理相同.

  • ? 问号表示 URL 中传递参数的开始。
  • content=我是被攻击者发布的微博 这是微博内容的参数。
  • & & 符号用于分隔多个参数。
  • visible=0 这是微博可见性的参数,0 表示公开。

width="0" height="0" 不是指屏幕分辨率,而是 HTML 标签的属性,用于设置图片的宽度和高度。这里将图片的宽度和高度都设置为 0,是为了让图片在页面上不可见,从而隐藏攻击行为。

攻击者通常通过以下几种方式诱导用户访问包含恶意代码的页面:

  1. 钓鱼网站: 攻击者创建一个看起来像合法网站的钓鱼网站,并在其中嵌入恶意代码。然后通过电子邮件、短信、社交媒体等方式诱导用户访问这个钓鱼网站。
  2. 恶意链接: 攻击者在论坛、博客、社交媒体等地方发布包含恶意链接的帖子或评论,诱导用户点击。
  3. XSS 漏洞: 如果目标网站存在 XSS(跨站脚本)漏洞,攻击者可以将恶意代码注入到目标网站的页面中。当其他用户访问这些被注入恶意代码的页面时,就会触发攻击。

如何防御 CSRF 攻击:

  1. 验证码: 在执行敏感操作之前,要求用户输入验证码。
  2. Referer 检查: 服务器检查请求的 Referer 字段,确保请求来自可信的来源。
  3. Token 验证: 服务器生成一个随机的 token,并将其存储在用户的 session 中。在执行敏感操作之前,要求用户提交这个 token。这就是该平台使用的 XSRF-TOKEN 的作用, 通常也会藏在 Cookie 中, 例如某个特定的字段, 就我们的抓包结果来看, 可能是 SUB 字段的一部分.
  • token: 这里的 token 指的是用于防御 CSRF 攻击的随机令牌,在该平台的例子中,就是 XSRF-TOKEN,通常它的值和 CookieSUB 字段的某一部分相同。
  • session: session 是一种服务器端的技术,用于跟踪用户的状态。服务器会为每个用户创建一个 session,并分配一个唯一的 session ID。session ID 通常存储在用户的 Cookie 中。

所以,st(或 XSRF-TOKEN)是用于防御 CSRF 攻击的 token,而 session ID 通常存储在 Cookie 中。它们之间有一定的关联,但不是同一个东西。

在 CSRF 攻击的例子中,Referer 的值取决于攻击者如何构建恶意页面。

  • 如果攻击者直接将恶意 标签嵌入到自己的网站中,那么 Referer 将是攻击者网站的地址。
  • 如果攻击者将恶意 标签嵌入到其他存在 XSS 漏洞的网站中,那么 Referer 将是该存在 XSS 漏洞的网站的地址。
  • 如果攻击者诱导用户直接点击恶意链接,那么 Referer 将为空,因为不是从其他页面跳转过来的。

需要注意的是,Referer 字段可以被伪造或禁用,因此它不能作为防御 CSRF 攻击的可靠手段。

关于 SUB 的解码:

我尝试使用 Base64 解码 SUB,但解码结果是乱码:؍)î\7g/XU'ŏ< ui A_5z=B[Ѝ-yБmML%쟕鮨ߒĚŶ % O̝

这是因为 SUB 并不是一个简单的 Base64 编码字符串,它还包含了一些其他信息。尝试使用 Base64 解码 SUB 的值得到乱码是正常的,因为 SUB 的值不仅仅是 Base64 编码那么简单. 它里面可能包含了一些二进制数据或者使用了其他的编码方式。

关于 Cookie 和 st 的过期时间:

我在脚本测试中发现 cookiest 又过期了, 所以我又重新发微博, 计算出前后两次失败的时间是 15-16 分钟左右, 所以目前预估这个域名 m.xxx.cncookiest 的过期时间大约是 15-20 分钟。后续我会继续通过实验和观察来验证这个过期时间。

提示: 可以设置一个定时任务,每隔一段时间(例如 10 分钟)自动使用脚本测试 Cookiest 的有效性。如果发现 Cookiest 过期,则发出提醒。

总结:

这篇博客记录了对 m.xxx.cn 发微博 API 的逆向工程过程,包括:

  • 使用浏览器开发者工具分析网络请求
  • 分析 POST 请求的参数和请求头
  • 使用 Python 的 requests 库构造 POST 请求并发送微博
  • st 参数和 Cookie 的关联性和过期时间的初步探索
  • 对 CSRF 攻击的原理和防御方法的介绍

通过这次逆向工程实践,我更深入地理解了 Web 开发的底层原理,也学习了如何使用 Python 进行网络编程和安全攻防。

未来的探索方向:

  • 继续研究 Cookiest 的生成和过期机制。
  • 探索该平台的其他 API 接口。
  • 学习更多关于 Web 安全和逆向工程的知识。

你可能感兴趣的:(python,网络爬虫,web安全,前端,数据分析,网络协议)