在Android手机上对https请求进行抓包

9,163 阅读10分钟

前段时间跟 QQ 群里的群友聊天时无意聊到了抓包的话题。抓包可以说是程序员日常开发调试问题的一个重要手段,可以帮助我们理清客户端与服务器之间的数据传输问题,以便于甩锅。

在过去,网络请求基本都是靠的 http 协议,那个时候的抓包是一件非常简单的事情。然而这几年,http 协议在逐渐被淘汰,几乎所有的网络请求都变成了 https 协议,这就使事情变得复杂了。

群里一位朋友说,https 是不可能被抓包的,不然怎么保证 https 传输的安全性,毕竟那么多大公司都在用这个协议来传输重要的数据。

这其实是一个比较有意思的话题,https 确实是非常安全的。但同时,https 也确实是可以抓包的,它们两者之间并不冲突。

考虑到仍然有许多朋友在这方面还有些不太了解,我准备写两篇文章来讲讲 https 抓包的相关知识。本篇文章先讲实践,教大家如何在 Android 手机上对 https 请求进行抓包。下一篇文章会讲原理,我们一起解析一下,为什么如此安全的 https 协议却仍然可以被抓包呢?

抓包工具的使用

要对网络请求进行抓包,首先肯定要选择一个抓包工具才行。

专业的抓包工具有很多,根据我的观察,国内的大多数开发者都比较喜欢用 Charles 这个工具来进行抓包。不过我个人更喜欢用 Fiddler 这个工具,而且我们平时工作时如果要进行抓包也都是用的 Fiddler。因为 Fiddler 和微软内部的日志分析工具是相互兼容的,并且 Fiddler 的作者也在微软工作。

那么本篇文章我都会以 Fiddler 这个工具来进行举例讲解,当然如果你习惯用 Charles 也完全没有问题,只是在工具的操作上可能会有所区别,原理是完全相同的。

首先需要在你的电脑上安装 Fiddler,这个工具是完全免费的,下载地址是:

www.telerik.com/fiddler/fid…

安装完成之后登录一下就可以使用了,它会自动抓取你当前这台电脑上的所有网络请求包。

但是如果我们想要抓取手机上的网络请求,那么还需要做点额外的配置才行。

首先从 Fiddler 顶部工具栏依次点击 View -> Preferences -> Connections,将会看到如下所示界面:


这里有两点需要注意,一个是端口号,默认值是 8866,如果没有什么特殊需求的话可以不用修改。

第二个是 Allow remote computers to connect 这个选项是一定要勾上的,不然手机上的网络请求将无法抓到。

勾上第二个选项,点击 SAVE,这样电脑端的配置就完成了。

接下来我们还需要在手机端进行一些简单的配置。

要确保的是,你的手机和用于抓包的这台电脑必须在同一个局域网下。

然后修改手机当前连接 Wifi 的高级选项,将代理类型改为手动,将代理主机名改成电脑的 ip 地址,将代理端口改成 8866,如下图所示:

点击保存即可。

完成以上配置之后,其实我们就可以使用 Fiddler 来对手机上的网络请求进行抓包了。不信你可以试一试在手机的浏览器上访问以下地址:

guolin.tech/api/china/

应该会看到如下界面:

然后再到 Fiddler 中查看一下,你就能发现刚才手机上的网络请求包已经成功被 Fiddler 抓到了(有时 Fiddler 中显示的包信息过多,不方便查看,可以使用 Ctrl+X 清空信息):

可以看到,这条网络请求的所有细节在 Fiddler 中一览无余,包括请求的头信息,响应的头信息,响应的 body 内容等等。

抓包工具将网络通讯的背后细节全部搬到了台面上,这样当客户端和服务器遇到联调问题时,到底是客户端的锅还是服务器的锅,看一看抓出来的包就全部清楚了。

以上就是抓包工具最传统的用法,然而这种用法现在已经不那么好使了,因为还在使用 http 协议的网络请求已经越来越少,绝大部分的网络请求都变成了 https 协议。

对 http 请求进行抓包

https 协议是一种加密传输的网络协议,所传输的数据不再是以明文的方式来传输,而都是加密过后再进行传输的。

这种协议保障了用户的数据安全,但对于抓包而言却是一件苦恼的事情。因为数据都加密了呀,我们抓到的包也都是一些密文信息,所以根本就无法用于定位问题。

比如我们可以尝试在手机浏览器中访问一下必应,然后观看 Fiddler 中抓到的包信息,如下图所示:

可以看到,Fiddler 虽然能够捕获到访问必应的网络请求,但是却无法解密出具体的传输内容,这种包对于我们分析问题并没有任何帮助。

那么对于 https 请求的网络包我们到底要怎么抓呢?别担心,Fiddler 是支持这个功能的,下面跟着我一步步操作就行。

首先需要在 Fiddler 中开启 https 抓包功能,从 Fiddler 顶部工具栏依次点击 View -> Preferences -> HTTPS。

在 HTTPS 设置页面中,先点击 Trust root certificate 来安装证书,然后勾选 Capture HTTPS traffic 选项,如下图所示:

点击 SAVE 保存,这样你就可以抓到电脑上 https 请求的包了。

但是手机上 https 请求的包我们还是抓不到的,你可以试试再次在手机上访问必应,将会看到如下界面:

出现这种错误基本都是证书的原因导致的,在下篇文章中我会详细分析这个错误出现的原因,本篇文章中我们先将它解决就好了。

在你的手机浏览器中访问如下地址:

ipv4.fiddler:8866/

将会看到一个由 Fiddler 内置的网页:

点击 FiddlerRoot certificate 这个链接,下载并安装由 Fiddler 提供的手机证书。

安装完成之后再次访问必应,你就会发现不会再报错了,而是可以正常显示出网页的内容:

然后观察 Fiddler,可以看到,请求必应首页的网络包也被成功抓到了,而且这次不再是密文,而是解密后的数据:

对 https 请求的抓包问题,就这样解决了!

对 Android 应用进行抓包

如此看来,https 抓包貌似也并不是一件难事。

没错,但还有一个细节需要大家注意。上述方案只适用于对浏览器中的网络请求进行抓包,如果你是想要对其他应用程序的网络请求抓包的话,仍然还是抓不到的。

为了证实这一点,我们就来新建一个应用程序,并编写一段最简单的网络请求代码,看看到底能不能抓到它发出的网络请求。

整个程序非常简单,我们在 MainActivity 中加入一个按钮,当点击按钮时就发起一个网络请求,代码如下所示:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val button: Button = findViewById(R.id.button)
        button.setOnClickListener {
        	sendRequest()
        }
    }

    private fun sendRequest() {
        thread {
            var connection: HttpURLConnection? = null
            try {
                val response = StringBuilder()
                val url = URL("https://www.bing.com")
                connection = url.openConnection() as HttpURLConnection
                val input = connection.inputStream
                val reader = BufferedReader(InputStreamReader(input))
                reader.use {
                    reader.forEachLine {
                        response.append(it)
                    }
                }
                Log.d("TAG", response.toString())
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                connection?.disconnect()
            }
        }
    }

}

没错,总共就这么多代码。但是不要忘记我们还得在 AndroidManifest.xml 中声明网络权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.capturetest">

    <uses-permission android: />
    ...

</manifest>

好了,现在来运行一下程序看一看效果吧。点击界面上的按钮,会向必应主页发起一条网络请求,然后观察 Fiddler 中的数据包:

可以看到,我们是无法像之前在浏览器中那样,成功抓到并解析出 https 请求的包信息的。

为什么会这样呢?这是因为 Android 在 7.0 系统中进行了一项安全升级。从 Android 7.0 系统开始,只是在手机上安装了抓包工具的证书,仍然是无法对 https 请求进行抓包的,还必须要在应用程序的代码中加入一段网络安全配置才行。

这项升级使得每个应用程序都变得更加安全,因为对 https 抓包确实是一个比较危险的行为,所有加密传输的数据都以明文的形式展示出来了。当然,如果是为了调试程序而抓包,这算是一个正当理由,但是你也理应只能对自己的程序进行抓包调试而已。如果只要在手机上安装了证书就可以对所有 App 的 https 请求进行抓包,那么无疑大大降低了这些 App 的安全性。

因此,Android 7.0 系统中才做了这项安全升级。默认情况下,我们无法对各个 App 的 https 请求进行抓包,如果你是想要对自己 App 的 https 请求抓包的话,那么可以这样做。

在 res/xml 目录下创建一个 network_security_config.xml 文件,然后加入如下配置:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="user"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

接下来还需要在 AndroidManifest.xml 中配置 android:networkSecurityConfig 属性来让上述配置生效:

<application
    ...
    android:networkSecurityConfig="@xml/network_security_config">
    ...
</application>

这样我们就可以对当前的应用程序发出的 https 网络请求进行抓包了。

重新运行一下程序,让我们再来试试吧,结果如下图所示:

结果正如我们预期的那样工作了。

最后一个小疑惑

那么本篇文章的内容到这里就差不多该结束了。

但是不知道大家有没有产生一个小疑惑,既然是从 Android 7.0 开始必须要在自己的应用程序中加入网络安全配置才能对 https 请求抓包,为什么我们一开始在浏览器中什么都没配,却也成功抓到了 https 请求的网络包呢?

这个问题其实让我困惑了很久,直到现在加入了微软 Edge 项目组才终于解开了这个疑惑。

Edge 是一款基于 Chromium 内核的浏览器,Chrome 也是,许多主流的浏览器都是。其实答案一直都在 Chromium 的源码中,只是我之前从来没有勇气去看过。

我们来查看一下 Chromium 源码中的 AndroidManifest.xml 文件,部分代码如下图所示:

可以看到,Chromium 的源码中也加入了一段 android:networkSecurityConfig 配置,那么我们继续跟进去看看里面到底配置了什么:

这不是和我们刚才在 Demo 中配置的内容一模一样吗?

自此真相大白了,原来之所以浏览器不需要做额外的配置也能对 https 请求进行抓包,是因为 Chromium 源码中已经对此做好了配置,而所有基于 Chromium 内核的浏览器也就都自动拥有了这个功能。

如果你想要在线查看 Chromium 的源码,可以访问这个地址:

source.chromium.org/

好了,本篇文章的内容就到这里。相信看完这篇文章,会对大家平时的网络开发与调试工作产生一定的帮助。

解决了怎么用的问题,接下来就要去了解原理了。下篇文章中我们来一起探讨一下为什么传说中如此安全的 https 协议却仍然可以被抓包呢?我们下期再见。

另外,如果想要学习 Kotlin 和最新的 Android 知识,可以参考我的新书 《第一行代码 第 3 版》

关注我的技术公众号“郭霖”,每个工作日都有优质技术文章推送。