美文网首页移动app测试典型bug积累
记一次手机本地时间修改引起的https请求失效的bug分享

记一次手机本地时间修改引起的https请求失效的bug分享

作者: 萧竹 | 来源:发表于2018-06-17 16:37 被阅读83次

    灵异bug发生

    某天广州银行直销银行APP在做兼容性测试的时候,用一个很久不用的的手机(三星 SM-C101)进行测试,发现app无法使用,提示网络异常。但是手机却是满格wifi,也没有设置代理,查看手机系统也是Android 4.2以上的(我们的产品支持的API是14以上,讲道理的话手机系统肯定也是支持的),但是的确每个页面都toast提示“网络连接异常”,实在是诡异。

    bug问题排查

    先看下网络是不是ok的吧,万能ping百度,百度能访问,说明我们的网络肯定是通的。只好抓包进一步看看是不是后端环境返回的网络异常,但是抓包工具上并没有抓到https请求,进一步地可以排除后端问题。此时非常困惑,为什么百度这些可以使用而我们的APP却无法访问网络呢,然后看了一下我们公司开发的同类的app紫金银行,也是无法使用网络,首先想到的会不会是兼容性问题,想了好久,网上也搜了好久,都没有找到这款旧手机相关的特别信息,历史bug列表也没有改款手机有过类似的bug。最后突然注意到了手机时间,手机时间是2015年1月1日,原来手机时间不知道被谁设置到2015年去了,2015年我们的app还未问世呢,我们APP使用的是https协议,如果手机修改了本地时间,自然是无法访问网络了!

    bug引入原因

    修改本地时间后,手机为什么https请求失效呢
    所以https与本机时间有什么关系呢?
    先来了解什么是https协议
    HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
    为了便于理解记忆,有个著名的小故事。

    第一话:古代某国边塞驻扎着10万大军。一日,狼烟四起,外敌来犯,为平息叛乱,皇帝派遣A将军去平息边关战事,临行前,皇帝给A将军一行送行,将一枚敕造铜质虎符的一半交与将军,说道:“虎符合并时,边关10万大军,凭此全符调遣,见符如见寡人,切记妥善保管,不可有丝毫缺损。”【对应图中3过程】
    第二话: 数日后,A将军抵达边塞大营,驻地B将军再三确认A将军身份,于是乎要求A将军拿出另一半虎符进行拼对,虎符无缝隙对接,确系A将军是皇帝派来的“定边大将”,可与B将军共同统领10万大军。【对应图中1、2、3过程】
    第三话:边关大军与来犯敌军两军对阵,排兵布阵通过鼓声号令,重鼓三声,大军前进30步,急鼓5声,方阵变换,骑兵出击,步兵合围。两军展开了激烈的对战……【对应图中5、6过程】


    https过程-引用.png

    https与本机时间关系
    https协议与本机时间相关,https传输的过程中是加密的,而加密的证书里面是携带有效期的,证书的有效期是一个固定的时间段。设备进行https连接的时候,是会携带当前的时间(本机时间)。如果时间不在这个有效期之内,https链接建立的时候,就会报网络无法连接的错误。

    问题解决

    解决方法1:修改本地手机时间

    知道原因后就好解决了,将手机时间设置为随网络时间设定,也就是符合现在的时间。所有的app都可以正常使用了。

    但是这个方法有点鸡肋,如果用户手机出现了这类问题,只能通过用户自己去发现问题再设置时间解决问题,用户都是懒的并且基本上是无IT相关知识的,基本上不太可能去排查问题的原因,此时则会面临这类用户的流失。

    解决方法2:在代码里面捕获异常,并返回异常提示

    如果在使用https证书导入时默认配置验证通过所有链接,这肯定是不安全的,但是某些手机由于内部时间并不是正常时间,在https证书的有效时间外,创建连接时,只想避免这一点时可以捕获时间验证的异常,使其不报错,能正确告诉用户是这是哪一类异常

    例如:
    (1)此异常是证书已经过期异常,在手机调到证书生效时间之后会捕捉到此异常,提示用户证书已过期
    (2)此异常是证书未生效异常,在手机调到证书生效时间之前会捕捉到此异常,提示用户手机当前时间与证书生效时间不符,请调整手机时间

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                  if (chain != null)
                      for (X509Certificate cert : chain) {
                          try {
                              // Make sure that it hasn't expired.
                              cert.checkValidity();
                          } catch (CertificateExpiredException e) {
                              //此异常是证书已经过期异常,在手机调到证书生效时间之后会捕捉到此异常
                              L.w(TAG, "checkServerTrusted: CertificateExpiredException:" + e.getLocalizedMessage());
                          } catch (CertificateNotYetValidException e) {
                              //此异常是证书未生效异常,在手机调到证书生效时间之前会捕捉到此异常
                              L.w(TAG, "checkServerTrusted: CertificateNotYetValidException:" + e.getLocalizedMessage());
                          }
                          try {
                              // Verify the certificate's public key chain.
                              cert.verify(cert.getPublicKey());
                          } catch (CertificateExpiredException e) {
                              //此异常是证书已经过期异常,在手机调到证书生效时间之后会捕捉到此异常
                              L.w(TAG, "checkServerTrusted: CertificateExpiredException:" + e.getLocalizedMessage());
                          } catch (CertificateNotYetValidException e) {
                              //此异常是证书未生效异常,在手机调到证书生效时间之前会捕捉到此异常
                              L.w(TAG, "checkServerTrusted: CertificateNotYetValidException:" + e.getLocalizedMessage());
                          } catch (CertificateException ex) {
                              //其他异常正常报错
                              L.w(TAG, "Throw checkClientTrusted: " + ex.getLocalizedMessage());
                              throw ex;
                          } catch (NoSuchAlgorithmException e) {
                              L.w(TAG, "checkServerTrusted: NoSuchAlgorithmException:" + e.getLocalizedMessage());
                          } catch (InvalidKeyException e) {
                              L.w(TAG, "checkServerTrusted: InvalidKeyException:" + e.getLocalizedMessage());
                          } catch (NoSuchProviderException e) {
                              L.w(TAG, "checkServerTrusted: NoSuchProviderException:" + e.getLocalizedMessage());
                          } catch (SignatureException e) {
                              L.w(TAG, "checkServerTrusted: SignatureException:" + e.getLocalizedMessage());
                          }
                      }
              }
    

    问题解决后带来的启发

    这个问题定位的时间过长且曲折,主要是因为测试对https协议工作原理不了解,所以作为测试童鞋,需要对网络协议原理、技术实现原理、网络基础知识都得掌握理解,才能在遇到问题的时候快速定位问题,并驱动开发解决之。

    另外,一个困扰自己超过2个小时的问题有必要整理下来,积累多了也是一份宝贵的财富,yeah!

    相关文章

      网友评论

        本文标题:记一次手机本地时间修改引起的https请求失效的bug分享

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