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

2021-06-02 09:32:36 浏览数 (1)

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

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

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

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

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

那么先从实践看起吧。

抓包工具的使用

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

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

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

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

https://www.telerik.com/fiddler/fiddler-everywhere

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

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

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

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

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

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

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

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

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

点击保存即可。

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

http://guolin.tech/api/china/

应该会看到如下界面:

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

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

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

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

对https请求进行抓包

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

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

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

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

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

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

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

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

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

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

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

http://ipv4.fiddler:8866/

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

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

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

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

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

对Android应用进行抓包

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

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

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

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

代码语言:javascript复制
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中声明网络权限:

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.capturetest">

    <uses-permission android:name="android.permission.INTERNET" />
    ...

</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文件,然后加入如下配置:

代码语言:javascript复制
<?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属性来让上述配置生效:

代码语言:javascript复制
<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的源码,可以访问这个地址:

https://source.chromium.org/

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

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

0 人点赞