如何使用未记录的 Web API

你好!几天前,我写了一篇关于小型个人程序的文章,我提到使用“秘密”未记录的 API 会很有趣,您需要将 cookie 从浏览器中复制出来才能访问它们。

有几个人问如何做到这一点,所以我想解释一下,因为它非常简单。我们还将讨论可能出现的问题、道德问题,以及这如何适用于您未记录的 API。

例如,让我们使用 Google Hangouts。我选择这个不是因为它是最有用的例子(我认为有一个官方 API 会更实用),而是因为许多实际有用的站点是更容易受到滥用的较小站点。因此,我们将使用 Google Hangouts,因为我 100% 确定 Google Hangouts 后端的设计能够适应这种闲逛。

让我们开始吧!

第 1 步:在开发人员工具中寻找有希望的 JSON 响应

我首先访问https://hangouts.google.com ,在 Firefox 开发人员工具中打开网络选项卡并查找 JSON 响应。您也可以使用 Chrome 开发者工具。

这就是它的样子

网络标签.png

如果请求在“类型”列中显示“json”,则该请求是一个很好的候选者”

我不得不环顾四周,直到发现一些有趣的东西,但最终我找到了一个“人”端点,它似乎返回了有关我的联系人的信息。听起来很有趣,让我们来看看。

第 2 步:复制为 cURL

接下来,我右键单击我感兴趣的请求,然后单击“复制”->“复制为 cURL”。

然后我将curl命令粘贴到我的终端中并运行它。这就是发生的事情。

 $ curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' -X POST ........ (a bunch of headers removed) Warning: Binary output can mess up your terminal. Use "--output -" to tell Warning: curl to output it to your terminal anyway, or consider "--output Warning: <FILE>" to save to a file.

你可能会想——这很奇怪,这个“二进制输出会弄乱你的终端”错误是什么?这是因为默认情况下,浏览器会向服务器发送Accept-Encoding: gzip, deflate header 以获取压缩输出。

我们可以通过将输出传递到gunzip来解压缩它,但我发现不发送该标头更简单。所以让我们删除一些不相关的标题。

第 3 步:删除不相关的标题

这是我从浏览器获得的完整curl命令行。这里有很多!我首先使用反斜杠 ( \ ) 拆分请求,以便每个标头位于不同的行上,以便于使用:

 curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' \ -X POST \ -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0' \ -H 'Accept: */*' \ -H 'Accept-Language: en' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'X-HTTP-Method-Override: GET' \ -H 'Authorization: SAPISIDHASH REDACTED' \ -H 'Cookie: REDACTED' -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'X-Goog-AuthUser: 0' \ -H 'Origin: https://hangouts.google.com' \ -H 'Connection: keep-alive' \ -H 'Referer: https://hangouts.google.com/' \ -H 'Sec-Fetch-Dest: empty' \ -H 'Sec-Fetch-Mode: cors' \ -H 'Sec-Fetch-Site: same-site' \ -H 'Sec-GPC: 1' \ -H 'DNT: 1' \ -H 'Pragma: no-cache' \ -H 'Cache-Control: no-cache' \ -H 'TE: trailers' \ --data-raw 'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=person.location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'

起初,这似乎是大量的东西,但在这个阶段你不需要考虑它的任何含义。您只需要删除不相关的行。

我通常只是通过反复试验找出可以删除哪些标头——我一直删除标头,直到请求开始失败。一般来说,您可能不需要Accept*RefererSec-*DNTUser-Agent和缓存标头。

在此示例中,我能够将请求缩减为:

 curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' \ -X POST \ -H 'Authorization: SAPISIDHASH REDACTED' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'Origin: https://hangouts.google.com' \ -H 'Cookie: REDACTED'\ --data-raw 'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=person.location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'

所以我只需要 4 个标头: AuthorizationContent-TypeOriginCookie 。这更易于管理。

第 4 步:将其翻译成 Python

现在我们知道我们需要什么标头,我们可以将curl命令转换为 Python 程序!这部分也是一个非常机械的过程,目标只是使用 Python 发送与使用 curl 完全相同的数据。

这就是它的样子。这与前面的curl命令完全相同,但使用 Python 的requests 。我还将很长的请求正文字符串分解为一个元组数组,以便更容易以编程方式使用。

 import requests import urllib data = [ ('personId','101777723'), # I redacted these IDs a bit too ('personId','117533904'), ('personId','111526653'), ('personId','116731406'), ('extensionSet.extensionNames','HANGOUTS_ADDITIONAL_DATA'), ('extensionSet.extensionNames','HANGOUTS_OFF_NETWORK_GAIA_GET'), ('extensionSet.extensionNames','HANGOUTS_PHONE_DATA'), ('includedProfileStates','ADMIN_BLOCKED'), ('includedProfileStates','DELETED'), ('includedProfileStates','PRIVATE_PROFILE'), ('mergedPersonSourceOptions.includeAffinity','CHAT_AUTOCOMPLETE'), ('coreIdParams.useRealtimeNotificationExpandedAcls','true'), ('requestMask.includeField.paths','person.email'), ('requestMask.includeField.paths','person.gender'), ('requestMask.includeField.paths','person.in_app_reachability'), ('requestMask.includeField.paths','person.metadata'), ('requestMask.includeField.paths','person.name'), ('requestMask.includeField.paths','person.phone'), ('requestMask.includeField.paths','person.photo'), ('requestMask.includeField.paths','person.read_only_profile_info'), ('requestMask.includeField.paths','person.organization'), ('requestMask.includeField.paths','person.location'), ('requestMask.includeField.paths','person.cover_photo'), ('requestMask.includeContainer','PROFILE'), ('requestMask.includeContainer','DOMAIN_PROFILE'), ('requestMask.includeContainer','CONTACT'), ('key','REDACTED') ] response = requests.post('https://people-pa.clients6.google.com/v2/people/?key=REDACTED', headers={ 'X-HTTP-Method-Override': 'GET', 'Authorization': 'SAPISIDHASH REDACTED', 'Content-Type': 'application/x-www-form-urlencoded', 'Origin': 'https://hangouts.google.com', 'Cookie': 'REDACTED', }, data=urllib.parse.urlencode(data), ) print(response.text)

我运行了这个程序,它工作了——它打印出一堆 JSON!万岁!

你会注意到我用REDACTED替换了一堆东西,那是因为如果我包含这些值,你可以访问我的帐户的 Google Hangouts API,这将是不好的。

我们完成了!

现在我可以修改 Python 程序来做任何我想做的事情,比如传递不同的参数或解析输出。

我不会用它做任何有趣的事情,因为我实际上根本对使用这个 API 不感兴趣,我只是想展示一下这个过程是什么样子的。

但是我们得到了一堆你绝对可以用它来做点什么的 JSON。

curlconverter 看起来很棒

有人评论说,您可以使用https://curlconverter.com/自动将 curl 翻译成 Python(以及许多其他语言!),这看起来很棒——我一直都是手动完成的。我在这个例子上试过了,它似乎工作得很好。

弄清楚 API 是如何工作的并非易事

我不想低估弄清楚未知 API 的工作原理是多么困难——这并不明显!我不知道这个 Google Hangouts API 的很多参数是做什么的!

但是很多时候有一些参数看起来很简单,比如requestMask.includeField.paths=person.email可能意味着“包括每个人的电子邮件地址”。所以我试着把注意力集中在我理解的参数上,而不是理解的参数上。

这总是有效的(理论上)

你们中的一些人可能想知道——你能一直这样做吗?

答案是肯定的——浏览器并不神奇!浏览器发送到后端的所有信息都只是 HTTP 请求。因此,如果我复制浏览器发送的所有 HTTP 标头,后端实际上无法判断请求不是由我的浏览器发送的,而是由随机 Python 程序发送的。

当然,我们删除了浏览器发送的一堆标头,因此理论上后端可以知道,但通常他们不会检查。

不过有一些注意事项——例如,许多谷歌服务的后端以一种完全难以理解(对我来说)的方式与前端通信,所以即使理论上你可以模仿他们正在做的事情,但实际上它可能几乎不可能的。

既然我们已经了解了如何使用此类未记录的 API,让我们来谈谈可能出错的一些事情。

问题 1:过期的会话 cookie

这里的一个大问题是我正在使用我的 Google 会话 cookie 进行身份验证,所以只要我的浏览器会话过期,这个脚本就会停止工作。

这意味着这种方法不适用于长时间运行的程序(我想使用真正的 API),但如果我只需要快速抓取一点数据作为 1-time 的东西,它可以很好地工作!

问题2:滥用

如果我使用的是小型网站,我的小 Python 脚本有可能会取消他们的服务,因为它处理的请求比他们能够处理的要多得多。因此,当我这样做时,我会尽量保持尊重,不要过快提出太多要求。

这一点尤其重要,因为许多没有官方 API 的站点都是资源较少的较小站点。

在这个例子中,这显然不是问题——我想我在写这篇博文时总共向 Google Hangouts 后端发出了 20 个请求,他们绝对可以处理。

此外,如果您过度使用您的帐户凭据访问 API 并导致问题,您可能(非常合理地)暂停您的帐户。

我还坚持下载属于我的或打算公开访问的数据——我不是在寻找漏洞。

请记住,任何人都可以使用您未记录的 API

我认为最重要的是要知道这实际上并不是如何使用其他人未记录的 API。这样做很有趣,但它有很多限制,我实际上并不经常这样做。

了解任何人都可以对您的后端 API 执行此操作更为重要!每个人都有开发者工具和网络标签,很容易看到你传递给后端的参数并改变它们。

因此,如果任何人都可以通过更改某些参数来获取其他用户的信息,那就不好了。我认为大多数构建公开可用 API 的开发人员都知道这一点,但我之所以提到它,是因为每个人都需要在某个时候第一次学习它 :)

来源分类:Z – Blog 2 Chinese
文章日期:March 11, 2022 at 10:09AM
收藏日期:March 11, 2022 at 10:09AM