如何让 Charles 抓取 Node.js 应用发出的 HTTPS 请求
Opened this issue · 0 comments
背景
- 我最近在用 Node.js 开发一款命令行工具,名为 “Kup”。
- Kup 底层使用 Got 这个库来发送请求,请求的接口都是 HTTPS 的。
- 我希望通过 Charles 来监听 Kup 与服务器之间的通信,完成一些调试工作。
- 本文理论上也适用于 Fiddler、Whistle 等基于代理的请求调试工具。
初步尝试
准备工作
- 在本机安装 Charles 的 CA 证书,并设置为 “Always Trust”。
- 打开 Charles,启动 HTTP 代理(
http://127.0.0.1:8888),确认已开启 “SSL Proxying”。
(这也是 Charles 抓取浏览器端 HTTPS 请求的准备工作。)
尝试一
把 Charles 代理设置为 macOS 全局代理。
在命令行运行 Kup。Charles 抓不到它发出的请求,失败。
尝试二
取消全局代理,改为精准指定终端代理配置:
export https_proxy=http://127.0.0.1:8888仍然抓不到,失败。
调研
在上面两次失败的尝试中,似乎 Kup 压根就没有走 Charles 的代理。一番调研后发现,想用 Charles 抓 Node.js 请求,基本上有两种方案:
-
第一种是我们熟悉的常规方案:就是让我们的 Node.js 应用(比如 Kup)通过 Charles 提供的代理来发请求。这与 Charles 抓浏览器请求的原理是一样的。
-
第二种属于变通方案:让 Charles 启动一个反向代理,把真正的请求地址包装一层,然后把 Node.js 应用的请求指向 Charles 反向代理包装之后的地址。这样也可以完成对请求的抓取。
第二种方案的原理看起来更复杂,但操作上却是相对简单的。因为这个方案不需要在本机安装 CA 证书,Node.js 应用端也只需要改改接口地址就可以了。这个方法临时用用是可以的,但它的缺陷也很明显:Node.js 应用端需要考虑两套地址的切换问题,另外 Charles 这一端也需要做针对性的配置,整个方案的可移植性不好。遂放弃之。
第一种方案在实际操作上要麻烦得多。原因是 Node.js 应用在发送请求时并不会自动走环境变量 https_proxy 指定的代理,这个行为需要由应用自行实现,而这个实现过程往往有点原始:一个支持代理的 HTTP 请求库(比如 Got)通常不会直接集成代理功能,而是允许我们指定一个 http.Agent 实例来作为它的代理;于是我们又需要通过专门的库来创建 http.Agent 实例……
虽然麻烦了点,但所幸这些操作都集中在 Node.js 应用这一端,纯代码就可以搞定;而且我们对这种抓包原理也更熟悉,所以最终还是选它。
最终实现
选好方案之后,实现起来似乎也不算很麻烦:
-
选用 https-proxy-agent 这个包来创建代理实例:
const HttpsProxyAgent = require('https-proxy-agent') const agent = new HttpsProxyAgent('http://127.0.0.1:8888')
-
把这代理实例提供给 Got:
got(myApi, { agent: { https: agent, // 指定 HTTPS 代理 }, })
-
由于 Charles CA 证书并不是个正经的证书,我们还需要给 Got 加个配置,忽略对证书的校验:
got(myApi, { agent: { https: agent, }, rejectUnauthorized: false, // 加这个选项 })
在此之后,每当 Kup 发出请求,Charles 就可以看到请求的具体情况了。成功!
当然,以上只是跑通流程的最小化实现。为了不影响用户的正常使用,上述代理行为需要限制在调试模式下,这些细节就不赘述了。
结语
我在这个问题上也是一个学习者,本文记录了我的探索过程与决策过程,希望能提供一些参考价值。
抛砖引玉,如果大家有更好的方案,或者上面有错漏,还请多多指教!
© 经验分享 · 日拱一卒 | Star = 收藏 | Watch = 订阅