原文地址:点击打开链接

=============================================================================================

你的Android HTTPS真的安全吗?

随着数据量级和维度的不断增加,数据安全已经成为移动互联网行业重点关注的领域。尤其是数据传输过程,是相对比较容易被拦截和嗅探的,因此谷歌对数据传输安全提出了更高的要求,推动Android开发者使用HTTPS来提升安全能力。但是现实中,Android开发人员对HTTPS的使用却存在一些问题。以下是TalkingData的研发总监卢健给大家做的分享。

背景

Google Play的开发者政策中心对“隐私和安全”有如下描述:

如果您的应用会处理用户个人数据或敏感数据(包括个人身份信息、财务和付款信息、身份验证信息、电话簿或联系人数据、麦克风和相机传感器数据,以及敏感的设备数据),则必须:

  • 同时在 Play Developer Console 中的指定位置以及通过 Play 分发的应用内提供隐私权政策。
  • 以安全无虞的方式处理用户数据,包括使用新型加密技术(例如通过HTTPS)传输数据。

随着数据量级和维度的不断增加,数据安全已经成为移动互联网行业重点关注的领域。尤其是数据传输过程,是相对比较容易被拦截和嗅探的,因此谷歌对数据传输安全提出了更高的要求,推动Android开发者使用HTTPS来提升安全能力。

但是现实中,Android开发人员对HTTPS的使用却存在一些问题,包括:

1.多数HTTPS是基于HttpClient来实现,但Android已不再默认支持HttpClient开发库,需要切换;

2.为了方便,开发人员一般会默认信任所有域名,这是个较大的安全隐患;

3.也是为了方便,证书校验也采用默认的设置,没有正确校验自签名证书;

关键还是在于如何正确校验证书和域名。

介绍Android HTTPS的文章有很多,比如谷歌官方Android Develop Training 中有一篇文章讲 Securitywith HTTPS and SSL,从基本概念和一个简单的HTTPS案例开始,帮助大家理解使用HTTPS可能遇到的问题以及应对方案,知识性很强。当然,阅读之前,最好已经掌握以下基础知识:

Java Security 基础知识
HTTPS 的通信原理
Android 网络基础
证书,证书文件格式
SSL TLS
秘钥管理,JSSE等

HTTPS 的简单例子

这里是 Google 提供的HTTPS例子:

URL url = newURL("https://wikipedia.org");
URLConnection urlConnection =url.openConnection();
InputStream in=urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);

以上代码表明,可以用HTTPS URL直接获取地址内容。隐含的意思是,服务器证书需要配置CA证书,不能是自签名证书,甚至不能是不知名的证书机构签发的证书,否则就会有异常。

关于证书,有几个概念说明:

CA证书:证书授权中心签发的证书,在访问这类网站的时候,浏览器地址栏的左端一般都有一个绿色盾牌。
不知名机构签发的证书:不是国际公认的签发机构,比如12306用的证书是中铁数字证书认证中心签发的。
自签名证书:Android应用程序开发最可能使用到,一般由服务器管理者生成。

平常大家使用自签名证书的情况比较多,因为自签名证书不仅免费,而且有效时间可以比较长。
但是使用自签名证书的时候,需要移动端开发人员自己实现校验代码,增加了开发的难度。尤其是如果开发人员没有经验,未对证书和域名做正确校验,则可能引入安全隐患。
这里重点讨论一下如何校验自签名证书和域名。

如何校验自签名证书

Google文章中关于如何让 HttpsURLConnection 信任自签名证书有如下代码(我们补充了中文注释):

// Load CAs from an InputStream // (could be from a resource or ByteArrayInputStreamor ...) // X.509 是Android唯一支持的证书格式 CertificateFactory cf =CertificateFactory.getInstance("X.509"); // From[https://www.washington.edu/itconnect/security/ca/load-der.crt](https://www.washington.edu/itconnect/security/ca/load-der.crt) InputStream caInput = newBufferedInputStream(new FileInputStream("load-der.crt")); // 证书的加载也可以是字符串的方式,建议使用字符串,并且对字符串做些额外的处理 // InputStream caInput=newByteArrayInputStream(cerString.getBytes());

Certificate ca; try { ca =cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); } finally { caInput.close(); }

// Create a KeyStore containing our trusted CAs // KeyStore 默认类型是 BKS,虽然 Android 的文档中的例子写了 JKS,但是 Android 并不支持JKS String keyStoreType =KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); // 加载一个默认的秘钥仓库,仓库是空的 keyStore.setCertificateEntry("ca",ca);

// Create a TrustManager that trusts the CAs inour KeyStore // TrustManager 是证书校验的关键,不使用系统默认校验方式时,需要开发者自己实现接口,完成校验代码 String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();// 默认算法 PKIX TrustManagerFactory tmf =TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore);

// Create an SSLContext that uses ourTrustManager // Android 不仅支持 TLS,还有 TLSv1.2 等,TLSv1.2需要 API levels 20+ // 更多选择可以参考[https://developer.android.com/re ... /ssl/SSLSocket.html](https://developer.android.com/reference/javax/net/ssl/SSLSocket.html) SSLContext context =SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(),null);

// Tell the URLConnection to use aSocketFactory from our SSLContext URL url = newURL("https://certs.cac.washington.edu/CAtest/"); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); // 证书校验的关键,设置SSLSocketFactory // 执行TrustManagerImpl 中 checkServerTrusted 方法完成服务器证书校验 urlConnection.setSSLSocketFactory(context.getSocketFactory()); InputStream in =urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);

以上代码,可以解决证书信任问题。但同时需要注意的是,这里是基于Android默认的信任检查来解决的。因为我们没有对TrustManager做任何修改,如果仍然遇到证书校验不通过的情况,则需要重新实现TrustManager,请用以下代码代替“tmf.getTrustManagers()”:

TrustManager tm = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() {return null; } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throwsCertificateException {} @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throwsCertificateException { // 方法直接返回, 将不对服务器证书做任何校验 // 确认服务器端证书颁发者和代码中的证书主体一致 if (!chain[0].getIssuerDN().equals(cert.getSubjectDN())) { } // 确认服务器端证书被代码中证书公钥签名 chain[0].verify(cert.getPublicKey()); // 确认服务器端证书没有过期 chain[0].checkValidity(); } }; context.init(null, new TrustManager[]{tm},null);

context.init(null, new TrustManager[]{tm},null);通过重新实现TrustManager,一定可以搞定证书校验。最好不要信任所有证书,这样会大大降低安全能力。

如何校验域名

在HttpsURLConnection中校验域名是比较简单的,这里Google提供了为URLConnection替换验证过程的例子:

// Create an HostnameVerifier that hardwiresthe expected hostname. // Note that is different than the URL'shostname: // example.com versus example.org // 这里强调的是URL中的域名和证书中不一致,所以默认的校验不能通过HostnameVerifier hostnameVerifier = newHostnameVerifier() { @Override publicboolean verify(String hostname, SSLSession session) { // 这段代码中 hostname 应该是 example.org // 获取默认的 HostnameVerifier HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); // 如果直接返回 true,等于不做域名校验 return hv.verify("example.com", session); } };

// Tell the URLConnection to use ourHostnameVerifier URL url = newURL("https://example.org/"); HttpsURLConnection urlConnection =(HttpsURLConnection)url.openConnection(); urlConnection.setHostnameVerifier(hostnameVerifier); InputStream in =urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);

正确使用域名校验很重要,直接返回true显然不安全。回调参数hostname是访问的域名,session可以通过getPeerCertificates获取到证书的相关信息,如果需要自己写代码完成域名校验,需要根据实际开发情况正确校验。

总结

用HttpsURLConnection来校验证书和域名的方法如下(不明确指定校验方式时,系统会采用默认的方式):

HttpsURLConnection urlConnection =(HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); urlConnection.setHostnameVerifier(hostnameVerifier);

校验的顺序是先校验证书,再校验域名。为了能够更好的理解Android HTTPS证书校验和域名校验的默认实现,可以参考Android的源代码:

  • 证书校验的默认实现:
    类:TrustManagerImpl.java

Git 获取源码:git clonehttps://android.googlesource.com/platform/external/conscrypt

  • 域名校验的默认实现:
    类:OkHostnameVerifier.java

Git 获取源码:git clonehttps://android.googlesource.com/platform/external/okhttp

这些知识也不算复杂,但是确实不少移动开发者容易忽视的。希望通过以上分享,能帮助开发者给移动用户提供一个安全的服务环境。

你的Android HTTPS真的安全吗?(转载)相关推荐

  1. Android优秀文章收集(转载)

    http://blog.csdn.net/u010375364/article/details/52200425 http://blog.csdn.net/u010375364/article/det ...

  2. 如何使用charles对Android Https进行抓包

    2019独角兽企业重金招聘Python工程师标准>>> 如何使用charles对Android Https进行抓包 博客分类: 搜索引擎,爬虫 原理 man-in-the-middl ...

  3. Android + https 实现 文件上传

    Android 应用中,如何使用https 实现 文件上传功能. package com.example.wbdream.zigvine;import android.annotation.Suppr ...

  4. 面试官系列 - https 真的安全吗,可以抓包吗,如何防止抓包吗

    文章目录 前言 背景 什么是 https 什么是SSL https 的连接过程 证书验证阶段 数据传输阶段 https 的加密方式是怎样的,对称加密和非对称加密,为什么要这样设计 内容传输为什么要使用 ...

  5. Android https 自签名和CA证书验证(基于OkHttp)

    Android HTTPS自签名和CA证书验证(基于OkHttp) HTTPS介绍 CA证书 自签名证书 问题描述 域名校验 OkHttp设置 总结 HTTPS介绍 HTTPS是一种通过计算机网络进行 ...

  6. Android开发真的要凉凉吗

    2008年,是中国互联网发展的10周年,同时也是智能手机的元年.在这一年,Google正式发布Andoid操作系统,统一了智能手机操作系统平台.凭借着移动互联发展的浪潮,智能手机应用软件(APP)快速 ...

  7. Android https抓包指南

    原文地址:见个人博客 为何写本文: 虽然过去有过多次抓包经验,但是长时间不做了,有些坑还是难以避免,比较浪费时间.所以沉淀一篇小白指南贴,节约时间. 如果要完成Android https的抓包和解析, ...

  8. https真的可以避免流量劫持吗

    http,一个使用了 20 多年的老协议.如今,网页技术有了长足的进步和发展,http 也开始逐渐被 https(即安装了SSL证书的网站)取代,在 http 中,很容易受到网络攻击,特别是流量劫持, ...

  9. Android实现XML解析技术 (转载http://www.cnblogs.com/hanyonglu/archive/2012/02/28/2370675.html)...

    本文介绍在Android平台中实现对XML的三种解析方式. XML在各种开发中都广泛应用,Android也不例外.作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能. 在 ...

  10. Android开源项目分类汇总-转载

    太长了,还是转载吧... 今天在看博客的时候,无意中发现了@Trinea在GitHub上的一个项目Android开源项目分类汇总,由于类容太多了,我没有一个个完整地看完,但是里面介绍的开源项目都非常有 ...

最新文章

  1. 日子是过以后、不是过从前
  2. SQL Server 备份与恢复之四:备份类型和选项
  3. 《Altium Designer 14电路设计与仿真从入门到精通》——1.4 Altium电路板总体设计流程...
  4. 浏览器的一个请求从发送到返回都经历了什么?
  5. Flex与Javascript交互
  6. oracle blob查重,如何解决oracle blob字段 的乱码问题
  7. Mysql学习总结(31)——MySql使用建议,尽量避免这些问题
  8. linux 安装 yum etcd,安装etcd - Go语言中文网 - Golang中文社区
  9. 布局--------动态添加 相对布局
  10. 绿色版Tomcat的配置
  11. [No00002D] “大学生还不如农民工”背后的“身份教育”困境
  12. iptv直播服务器维护,IPTV机顶盒的智能化配置和维护方案介绍【详解】
  13. | 码教授创始人倾情回馈母校四川大学
  14. device unauthorized adbd cannot run as root in production builds insufficient permissions
  15. 如何在CentOS8服务器上启用PowerTools
  16. java dwr 漏洞_DWR异常情况处理常见方法解析
  17. Android按钮滚动条,Android自定义Seekbar滑动条,Pop提示跟随滑动按钮一起滑动
  18. 斐讯k2刷不死breed K2 22.5.11.14
  19. 树莓派4 安装 UbuntuServer20.04.1X64 位系统 配置 wify
  20. 红叶李之Linux bash 基础特性

热门文章

  1. android三国2,三国演义2单机版安卓
  2. MapGuide/AIMS在台湾?
  3. 2.3,2.2,2.1最新谷歌服务包发布附下载(原创)
  4. [转] 基于MBR 的bootkit的进展 鬼影-TDL4-BMW
  5. WindowsNT/2000的系统日志文件
  6. windows server 2000系统安装
  7. tomcat Failed creating java C:\Program Files\Java\jre6\bin\client\jvm.dll %1 不是有效的 Win32 应用程序。...
  8. mysql报错无效默认值1067_Mysql 报错:#1067
  9. 证书制作,各个环节的原理以及推送证书制作,和如何为推送服务器提供证书
  10. 十、非参数检验:使用python进行卡方拟合优度检验