美文网首页Android开发Android技术知识
用了Https防抓包就真的安全了?带你领会其中奥秘!

用了Https防抓包就真的安全了?带你领会其中奥秘!

作者: 码农的地中海 | 来源:发表于2022-06-14 21:57 被阅读0次

    HTTPS的定义

    简而言之,HTTPS可以理解为HTTP+TLS,TLS是传输层加密协议,是HTTPS安全的核心,其前身是SSL 。通过对数据传输层进行加密,可以有效防止被第三方黑客、抓包软件截获后解密的行为,从而极大地保护了用户访问网络的安全性。

    • 数据明文传输,易嗅探

    • 数据完整性无验证,易篡改

    • 网站身份无认证,易假冒

    a602bd1809682f8257e36e2cf3465155.jpeg

    HTTPS工作原理

    大家可能都听说过 HTTPS 协议之所以是安全的是因为 HTTPS 协议会对传输的数据进行加密,而加密过程是使用了非对称加密实现。

    但其实,HTTPS 在内容传输的加密上使用的是对称加密,非对称加密只作用在证书验证阶段。

    HTTPS 的整体过程分为证书验证和数据传输阶段,具体的交互过程如下:

    QQ截图20220614214000.png

    证书验证阶段:

    • 浏览器发起 HTTPS 请求。

    • 服务端返回 HTTPS 证书。

    • 客户端验证证书是否合法,如果不合法则提示告警。

    数据传输阶段:

    • 当证书验证合法后,在本地生成随机数。

    • 通过公钥加密随机数,并把加密后的随机数传输到服务端。

    • 服务端通过私钥对随机数进行解密。

    • 服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输。

    单向/双向验证

    服务器是使用的HTTPS协议传输,并且买了权威机构的CA证书。但是客户端在发送请求时,未使用证书验证方式,这样就会存在中间人攻击的可能。所以说之前我的那种做法是不安全的。

    单向验证

    单向认证:保证server是真的,通道是安全的(对称密钥),校验证书,不可以抓包。

    流程:

    1.客户端向服务器端发送SSL协议版本号、加密算法种类、随机数等信息。

    2.服务端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书。

    3.客户端使用服务端返回的信息验证服务器的合法性,包括

    • 证书是否过期

    • 发行服务器证书的CA是否可靠

    • 返回的公钥是否能正确解开返回证书中的数字签名

    • 服务器证书上的域名是否和服务器的实际域名相匹配

    • 验证通过后,将继续进行通信,否则,终止通信

    4.客户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择。

    5.服务器端在客户端提供的加密方案中选择加密程度最高的加密方式。

    6.服务器将选择好的加密方案通过明文方式返回给客户端。

    7.客户端接收到服务器端返回的加密方式后,使用该加密方式生成产生随机码,用作通信过程中对称加密的秘钥,使用服务器端返回的公钥进行加密,将加密后的随机码发送至服务器。

    8.服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获取对称加密秘钥。

    9.在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。

    准备

    向我们亲爱的后台小伙伴要下他们申请的证书,.crt或者.pem的都可以,然后转成.cer证书,将.cer文件导入工程。

    QQ截图20220614214018.png

    AFHTTPSessionManager中按照上图进行写。注意AFHTTPSessionManager要用initWithBaseURL初始化,这样,单项验证就完了。**

    双向验证

    保证client和server是真的,通道是安全的(对称密钥)

    流程

    1.客户的浏览器向服务器传递客户端SSL协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息。

    2.服务器向客户端传送SSL协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己的证书。

    3.客户利用服务器传过来的信息验证服务器的合法性,服务器的合法性包括

    • 证书是否过期

    • 发行服务器证书的CA是否可靠

    • 发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”

    • 服务器证书上的域名是否和服务器的实际域名相匹配

    • 如果合法性验证没有通过,通讯将断开,如果合法性验证通过,将继续进行第四步。

    4.用户端随机产生一个用于后面通讯的“对称密码”,然后用服务器的公钥(服务器的公钥从步骤2终端服务器的证书中获得)对其加密,然后将加密后的“预主密码”传给服务器。

    5.如果服务器要求客户的身份认证(在握手过程中为可选),用户可以建立一个随机数然后对其进行数据签名,将这个含有签名的随机数和客户自己的证书以及加密过的“预主密码”一起传给服务器。

    6.如果服务器要求客户的身份认证,服务器必须检验客户证书和签名随机数的合法性,具体的合法性验证过程包括:

    客户的证书使用日期是否有效

    为客户提供证书的CA是否可靠

    发行CA的公钥能否正确解开客户证书的发行CA的数字签名

    检查客户的证书是否在证书废止列表(CRL)中。

    检验如果没有通过,通讯立刻中断;如果验证通过,服务器将用自己的私钥解开加密的“预主密码”,然后执行一系列步骤来产生主通讯密码(客户端也将通过同样的方法产生相同的主通讯密码)。

    时在SSL通讯过程中还要完成数据通讯的完整性,防止数据通讯中的任何变化。

    8.客户端向服务器端发出信息,指明后面的数据通讯将使用步骤7中的主密码为对称密钥,同时通知服务器客户端的握手过程结束。

    9.服务器向客户端发出信息,指明后面的数据通讯将使用的步骤7中的主密码为对称密钥,同时通知客户端服务器端的握手过程结束。

    10.SSL的握手部分结束,SSL安全通道的数据通讯开始,客户和服务器开始使用相同的对称密钥进行数据通讯,同时进行通讯完整性的检验。

    双向验证的实例代码:


    QQ截图20220614214046.png QQ截图20220614214055.png

    一般应用都是采用单向认证的,原因很简单,用户数目广泛,且无需做在通讯层做用户身份验证,一般都在应用逻辑成来保护用户的合法登入。但如果是企业对应对接,情况就不一样,可能会要求对客户端做身份验证。这时就需要做双向认证。

    差异:从单向认证和双向认证的流程来看,单向认证只要求站点部署了SSL证书就行,任何用户都可以去访问(IP被限制除外等),只是服务器端提供了身份认证。而双向认证则是需要服务端需要客户端提供身份认证,只能是服务端允许的客户能去访问,安全性相对要高一些。

    常用抓包工具

    • Charles

    • Fiddler

    如何防止抓包

    对于HTTPS API接口,如何防止抓包呢?既然问题出在证书信任问题上,那么解决方法就是在我们的APP中预置证书。在TLS/SSL握手时,用预置在本地的证书中的公钥校验服务器的数字签名,只有签名通过才能成功握手。由于数字签名是使用私钥生成的,而私钥只掌握在我们手上,中间人无法伪造一个有效的签名,因此攻击失败,无法抓包。

    这样做虽然解决了抓包问题,但是也带来了另外一个问题:我们购买的证书都是有有效期的,到期前需要对证书进行更新。主要有两种方式:

    1、提供预置证书更新接口。在当前证书快过期时,APP请求获取新的预置证书,这过渡时期,两个证书同时有效,直到安全完成证书切换。这种方式有一定的维护成本,且不易测试。 2、在APP中只预埋公钥,这样只要私钥不变,即使证书更新也不用更新该公钥。但是,这样不太符合周期性更新私钥的安全审计需求。一个折中的方法是,一次性预置多个公钥,只要任意一个公钥验证通过即可。考虑到我们的证书一般购买周期是3-5年,那么3个公钥,可以使用9-15年,同时,我们在此期间还可以发布新版本废弃老公钥,添加新公钥,这样可以使公钥一直更新下去。

    防止抓包全过程

    Android中如何访问HTTPS呢,其实Retrofit、OkHttp均支持HTTPS的访问 项目中引入网络库,以implementation 'com.squareup.okhttp3:okhttp:4.2.0'为例,

    final OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
    final Request request = new Request.Builder()
            .url("https://www.baidu.com/robots.txt")
            .build();
    final Response execute = okHttpClient.newCall(request).execute();
    final String bodyStr = execute.body().string();
    Log.d(TAG, bodyStr);
    复制代码
    

    那如果关闭客户端的CA证书,GlobalSign Root CA-R1,相当于不信任百度服务器的数字证书,会导致报错

    Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 通过手动将GlobalSign Root CA-R1.cer放入项目中的assets文件夹,则可避免这一错误,如何引用项目中集成的证书呢? 通过

    SSLContext sslContext;
            try {
                InputStream inputStream = getAssets().open("");
                sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{OkhttpU.trustManagerForCertificates(inputStream)}, null);
                SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
                OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, OkhttpU.trustManagerForCertificates(inputStream)).build();
                final Request request = new Request.Builder()
                        .url("https://www.baidu.com/robots.txt")
                        .build();
                final Response execute = okHttpClient.newCall(request).execute();
                final String bodyStr = execute.body().string();
                Log.d(TAG, bodyStr);
            } catch (Exception e) {
                e.printStackTrace();
            }
    复制代码
    public class OkhttpU {
    
        public static X509TrustManager trustManagerForCertificates(InputStream in)
                throws GeneralSecurityException {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
            if (certificates.isEmpty()) {
                throw new IllegalArgumentException("expected non-empty set of trusted certificates");
            }
    
            // Put the certificates a key store.
            char[] password = "password".toCharArray(); // Any password will work.
            KeyStore keyStore = newEmptyKeyStore(password);
            int index = 0;
            for (Certificate certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificate);
            }
    
            // Use it to build an X509 trust manager.
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
                    KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, password);
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            }
            return (X509TrustManager) trustManagers[0];
        }
    
        private static KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
            try {
                KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                InputStream in = null; // By convention, 'null' creates an empty key store.
                keyStore.load(in, password);
                return keyStore;
            } catch (IOException e) {
                throw new AssertionError(e);
            }
        }
    }
    
    复制代码
    

    就可以正常访问https了

    综上,通过引入自定义证书,然后给OkHttp设置sslSocketFactory可以有效的防止抓包,但是.cer放到assets下很容易被反编译,可以通过jdk下的命令keytool -printcert -rfc -file srca.cer导出字符串,然后通过

    OkHttpClientManager.getInstance()
                    .setCertificates(new Buffer()
                            .writeUtf8(CER_STRING) //CER_STRING是到处的string常量
                            .inputStream());
    

    总结

    总的来说https把流量加密了,正常抓包,你看到的内容是一堆乱码。 https的加密没有安全问题,但它只是用来防止通信过程中被第三方获取明文。如果黑客能直接控制通信的双方(你的电脑,或服务器),那么黑客肯定能看到https明文的。

    Android技术交流与资料学习:Android核心技术进阶手册、实战笔记、面试题纲资料

    放抓包策略就是对抗hook,常见方法:

    • 检测hook : 检测Xposed、Frida、Substrate等Hook框架

    • 使用socket连接 : 使用Socket走TCP/UDP,防止被应用层抓包

    • 传输数据加密 :协议字段加密传输,并隐藏秘钥,应用层加固

    • native层传输 : 将网络传输逻辑写到jni层实现,提高反编译门槛

    相关文章

      网友评论

        本文标题:用了Https防抓包就真的安全了?带你领会其中奥秘!

        本文链接:https://www.haomeiwen.com/subject/kgqtvrtx.html