Https

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。这个系统的最初研发由网景公司(Netscape)进行,并内置于其浏览器Netscape Navigator中,提供了身份验证与加密通讯方法。现在它被广泛用于万维网上安全敏感的通讯,例如交易支付方面。(注:本段来自百度百科)

起因

前段时间,同事拿着一个代码安全扫描出来的 bug 过来咨询,我一看原来是个 https通信时数字证书校验的漏洞,一想就明白了大概;其实这种问题早两年就有大规模的暴露,各大厂商App也纷纷中招,想不到过了这么久某大厂客户端里还留有这种坑;然后仔细研究了漏洞所在的代码片段,原来所属的是新浪微博分享 sdk 内部的,因为这个 sdk是源码引用的,一直没有更新,年久失修,所以也就被扫描出来了。
因此给出的解决方案是:
先获取最新的 sdk,看其内部是否已解决,已解决的话升级 sdk 版本即可;
第1步行不通,那就自己写校验逻辑,猫客全局通信基本已经使用 https 通信,参考着再写一遍校验逻辑也不是问题;
后来查了一下网上信息,早在2014年10月份,乌云平台里就已经暴露过这个漏洞,想必当时一定是忙于双十一忽略了这个问题。
虽然这个问题通过升级 sdk解决了,但是这个问题纯粹是由于开发者本身疏忽造成的;特别是对于初级开发人员来说,可能为了解决异常,屏蔽了校验逻辑;所以我还是抽空再 review了一下这个漏洞,整理相关信息。

问题描述

对于数字证书相关概念、Android 里 https 通信代码就不再复述了,直接讲问题。缺少相应的安全校验很容易导致中间人攻击,而漏洞的形式主要有以下3种:

自定义X509TrustManager

在使用HttpsURLConnection发起 HTTPS 请求的时候,提供了一个自定义的X509TrustManager,未实现安全校验逻辑,下面片段就是当时新浪微博 sdk 内部的代码片段。如果不提供自定义X509TrustManager,代码运行起来可能会报异常(原因下文解释),初学者就很容易在不明真相的情况下提供了一个自定义的X509TrustManager,却忘记正确地实现相应的方法。本文重点介绍这种场景的处理方式。这里引用部分相关代码:

TrustManager tm = new X509TrustManager() {public void checkClientTrusted(X509Certificate[] chain, String authType)throws CertificateException {//do nothing,接受任意客户端证书}public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {//do nothing,接受任意服务端证书}public X509Certificate[] getAcceptedIssuers() {return null;}
};sslContext.init(null, new TrustManager[] { tm }, null);

自定义HostnameVerifier

在握手期间,如果 URL 的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口的实现程序来确定是否应该允许此连接。如果回调内实现不恰当,默认接受所有域名,则有安全风险。

HostnameVerifier hnv = new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {// Always return true,接受任意域名服务器return true;}
};
HttpsURLConnection.setDefaultHostnameVerifier(hnv);

如上,如果不做任何的教研就是有风险的。

SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

修复方案

分而治之,针对不同的漏洞点分别描述,这里就讲的修复方案主要是针对非浏览器App,非浏览器 App 的服务端通信对象比较固定,一般都是自家服务器,可以做很多特定场景的定制化校验。如果是浏览器 App,校验策略就有更通用一些。前面说到,当发起 HTTPS 请求时,可能抛起一个异常,以上面说到的代码来看:

try {URL url = new URL("https://certs.cac.washington.edu/CAtest/");URLConnection urlConnection = url.openConnection();InputStream in = urlConnection.getInputStream();copyInputStreamToOutputStream(in, System.out);
} catch (MalformedURLException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();
}
private void copyInputStreamToOutputStream(InputStream in, PrintStream out) throws IOException {byte[] buffer = new byte[1024];int c = 0;while ((c = in.read(buffer)) != -1) {out.write(buffer, 0, c);}
}

它会抛出一个SSLHandshakeException的异常。这里截取部分异常。

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.....//省略n多错误at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:318)... 10 more
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.... 16 more

解决方案一

不论是权威机构颁发的证书还是自签名的,打包一份到 app 内部,比如存放在 asset 里。通过这份内置的证书初始化一个KeyStore,然后用这个KeyStore去引导生成的TrustManager来提供验证,具体代码如下:

try {CertificateFactory cf = CertificateFactory.getInstance("X.509");// uwca.crt 打包在 asset 中,该证书可以从https://itconnect.uw.edu/security/securing-computer/install/safari-os-x/下载InputStream caInput = new BufferedInputStream(getAssets().open("uwca.crt"));Certificate ca;try {ca = cf.generateCertificate(caInput);Log.i("Longer", "ca=" + ((X509Certificate) ca).getSubjectDN());Log.i("Longer", "key=" + ((X509Certificate) ca).getPublicKey();} finally {caInput.close();}// Create a KeyStore containing our trusted CAsString keyStoreType = KeyStore.getDefaultType();KeyStore keyStore = KeyStore.getInstance(keyStoreType);keyStore.load(null, null);keyStore.setCertificateEntry("ca", ca);// Create a TrustManager that trusts the CAs in our KeyStoreString tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);tmf.init(keyStore);// Create an SSLContext that uses our TrustManagerSSLContext context = SSLContext.getInstance("TLSv1","AndroidOpenSSL");context.init(null, tmf.getTrustManagers(), null);URL url = new URL("https://certs.cac.washington.edu/CAtest/");HttpsURLConnection urlConnection =(HttpsURLConnection)url.openConnection();urlConnection.setSSLSocketFactory(context.getSocketFactory());InputStream in = urlConnection.getInputStream();copyInputStreamToOutputStream(in, System.out);
} catch (CertificateException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();
} catch (NoSuchAlgorithmException e) {e.printStackTrace();
} catch (KeyStoreException e) {e.printStackTrace();
} catch (KeyManagementException e) {e.printStackTrace();
} catch (NoSuchProviderException e) {e.printStackTrace();
}

这样访问非“https://certs.cac.washington.edu/CAtest/”就会报SSLHandshakeException。也就是说对于特定证书生成的TrustManager,只能验证与特定服务器建立安全链接,这样就提高了安全性。

解决方案2

同方案1,打包一份到证书到 app 内部,但不通过KeyStore去引导生成的TrustManager,而是干脆直接自定义一个TrustManager,自己实现校验逻辑;
校验逻辑主要包括:
•服务器证书是否过期
•证书签名是否合法

try {CertificateFactory cf = CertificateFactory.getInstance("X.509");// uwca.crt 打包在 asset 中,该证书可以从https://itconnect.uw.edu/security/securing-computer/install/safari-os-x/下载InputStream caInput = new BufferedInputStream(getAssets().open("uwca.crt"));final Certificate ca;try {ca = cf.generateCertificate(caInput);Log.i("Longer", "ca=" + ((X509Certificate) ca).getSubjectDN());Log.i("Longer", "key=" + ((X509Certificate) ca).getPublicKey());} finally {caInput.close();}// Create an SSLContext that uses our TrustManagerSSLContext context = SSLContext.getInstance("TLSv1","AndroidOpenSSL");context.init(null, new TrustManager[]{new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain,String authType)throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] chain,String authType)throws CertificateException {for (X509Certificate cert : chain) {// Make sure that it hasn't expired.cert.checkValidity();// Verify the certificate's public key chain.try {cert.verify(((X509Certificate) ca).getPublicKey());} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchProviderException e) {e.printStackTrace();} catch (SignatureException e) {e.printStackTrace();}}}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}}}, null);URL url = new URL("https://certs.cac.washington.edu/CAtest/");HttpsURLConnection urlConnection =(HttpsURLConnection)url.openConnection();urlConnection.setSSLSocketFactory(context.getSocketFactory());InputStream in = urlConnection.getInputStream();copyInputStreamToOutputStream(in, System.out);
} catch (CertificateException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();
} catch (NoSuchAlgorithmException e) {e.printStackTrace();
} catch (KeyManagementException e) {e.printStackTrace();
} catch (NoSuchProviderException e) {e.printStackTrace();
}

同样上述代码只能访问 certs.cac.washington.edu 相关域名地址,如果访问 其他网址 ,则会在cert.verify(((X509Certificate) ca).getPublicKey());处抛异常,导致连接失败。

自定义HostnameVerifier,建立匹配规则;业务复杂的话,还可以结合配置中心、白名单、黑名单、正则匹配等多级别动态校验;总体来说逻辑还是比较简单的,反正只要正确地实现那个方法。

HostnameVerifier hnv = new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {//示例if("yourhostname".equals(hostname)){  return true;  } else {  HostnameVerifier hv =HttpsURLConnection.getDefaultHostnameVerifier();return hv.verify(hostname, session);}}
};

主机名验证策略改成严格模式:

SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);

这样就有效的避免了Hook。

Android通信安全之HTTPS相关推荐

  1. 关于 Android okhttp 使用 HTTPS 的学习记录

    起因 之前因为手机应用的安全性问题特意的组织讨论了一下,鉴于项目特性(功能验证),并非是实际场景应用,以及使用加密算法如果一旦数量级过高可能会造成服务器的负担,所以初步考虑先把 HTTPS 连接调通, ...

  2. Android逆向之https,android实战项目源码

    超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用层最为广泛的一种网络协议. 发展史 协议 发展 说明 HTTP/0.9 1991年定稿最早的H ...

  3. Android应用实现Https双向认证

    为什么需要双向认证 Https保证的是信道的安全,即客户端和服务端通信报文的安全.但是无法保证中间人攻击,所以双向认证解决的问题就是防止中间人攻击. 中间人攻击(Man-in-the-MiddleAt ...

  4. 用Fiddler在Android上抓HTTPS包

    iddler是一个HTTP协议调试代理工具,在开发网络应用的时候经常会用到,其最基本的作用是能抓HTTP的数据包,当然它还有更高级的用法,如添加断点.修改请求与相应的数据等等... 简单说下使用方法: ...

  5. android 9.0 https 适配,如何适配 Android 9.0? 在 Android 9.0 上发生 SSL handshake timed out 异常怎么解决...

    Android 9.0 开始,默认不允许明文传输,所以在建立网络连接时会使用 https 连接,同时进行安全认证.如果应用没有做对应处理,即会发生上述异常. 解决方法有两种: 一. 在应用里声明允许明 ...

  6. android httpClient 支持HTTPS的访问方式

    项目中Android https请求地址遇到了这个异常,javax.net.ssl.SSLPeerUnverifiedException: No peer certificate,是SSL协议中没有终 ...

  7. android中进行https连接的方式的详解

    比较不错的帖子哦 android Video Buffer傳輸流程 http://www.eoeandroid.com/thread-195728-1-1.html 利用反射机制,封装 SAX解析类, ...

  8. Mac下用Charles实现Android http和https抓包

    背景 工作以后,团队需要,抓包工具用的特别多.Charles功能丰富,能满足需要抓包的大多数场景. 之所以专门讲一下Android抓包,一个原因是笔者从事Android开发,经常用:另外,就是网上关于 ...

  9. 解决安卓android加载https页面不能正常显示问题

    问题 当load有ssl层的https页面时,如果这个网站的安全证书在Android无法得到认证,WebView就会变成一个空白页:那么,我们就要针对这一状况作出对应的处理. 处理 设置WebView ...

最新文章

  1. 微信小程序使用npm 进行下载构建组价
  2. (转)基于MVC4+EasyUI的Web开发框架形成之旅--界面控件的使用
  3. iOS 绘画 点与像素
  4. Objective-C:GCC+GNUstep配置
  5. 台式计算机怎么加一个硬盘,如何再安装一个台式计算机硬盘驱动器?如何在计算机安装中添加额外的硬盘...
  6. KeyMob聚合平台--国内口碑最好的移动广告聚合平台
  7. vue2.0 组件通信
  8. OSI七层模型每层的功能及包含的协议
  9. php post api json数据,php – REST API:请求身份为JSON或纯POST数据?
  10. ffmpeg合并音频(转)
  11. 根据进程名判断该进程是否存在(C++)
  12. POP3协议(电子邮件邮局协议)中UIDL和TOP命令在实际使用中的作用
  13. css3技巧——产品列表之鼠标滑过效果translateY(三)
  14. 树莓派开机启动chromium浏览器
  15. CC2630 7x7 更改为5X5
  16. 【趣文】我是一个线程
  17. 平阳县抖音平台直播运营主播带货第三期培训提升班开班啦!
  18. mysql命令行集锦
  19. 假设检验中的P值 与显著性水平的联系
  20. 二叉树4:二叉树求树高度(超级详细)

热门文章

  1. minio数据迁移工具rclone
  2. Android手势识别——上下左右滑动、屏幕上下左右中区域处理
  3. php面向对象开源_回到学校:5个面向学生和老师的开源程序
  4. 健身网站php,基于WEB的健身网站的设计与实现
  5. 上项线体表位置_颅骨体表定位标志
  6. 做大数据风控,需要做哪些准备?
  7. html打印预览出现重叠,打印预览下面的怎么会重叠在上面
  8. hp计算机如何重装win7,为你解答惠普电脑怎么重装win7系统
  9. (附源码)计算机毕业设计SSM科技类产品众筹系统
  10. gitLab数据备份和恢复