1. 使用Webview进行HTTPs通信

Android系统内置了一些可信机构办法的证书,可用于作HTTPs证书校验。实际上,使用Webview组件进行HTTPs通信,其证书验证环节也是系统默认会去做的。若发现证书不合法,Webview将显示一个空白页面,其错误在onReceivedSslError()这个方法里进行处理。使用Webview进行HTTPs通信应当遵循如下安全规范:
1) onReceivedSslError()方法里不能简单地用proceed()方法进行处理,建议给用户一定的提示(如“SSL证书错误,是否继续连接”等)。
这里给出错误的代码示例。

public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error){// 只简单地调用proceed()方法,忽略证书错误问题paramSslErrorHandler.proceed();
}

一般来说,使用Webview连接带有可信机构颁发证书的HTTPs站点,onReceivedSslError()方法里无需作任何处理(系统默认是拒绝连接的),这里给出正确的示例代码。

public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error){// Do nothing// 效果同 paramSslErrorHandler.cancel();
}

2. X509TrustManager

X509TrustManager用于实现SSL证书的安全校验,若使用不当,将导致APP对SSL证书不作校验,从而使黑客有了中间人攻击的可乘之机。开发者经常犯的错误有如下:
 自定义X509TrustManager,且不做任何校验逻辑,一般为空实现;
这里给出常见的自定义X509TrustManager的错误示例代码(以使用HttpsURLConnection进行HTTPs连接的情况为例)。

SSLContext localSSLContext = SSLContext.getInstance("TLS");
TrustManager[] arrayOfTrustManager = new TrustManager[1];
// 自定义X509TrustManager
arrayOfTrustManager[0] = new MyTrustManager();
localSSLContext.init(null, arrayOfTrustManager, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(localSSLContext.getSocketFactory());
HttpsURLConnection localHttpsURLConnection = (HttpsURLConnection) new
URL(paramString).openConnection();
……
class MyTrustManager implements X509TrustManager{// 不作任何校验public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {}// 不作任何校验public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {}// 返回nullpublic X509Certificate[] getAcceptedIssuers(){return null;}
}

使用上述方式进行HTTPs通信,将存在中间人攻击的可能。由于缺乏证书校验机制,黑客可以通过使用自签名证书,结合DNS欺骗,使用户访问恶意页面。因此,使用HttpsURLConnection或HttpClient进行HTTPs通信时,需要验证证书的合法性。这里把HTTPs站点作分类:
A. 带有可信机构颁发证书的HTTPs站点;
B. 带有自签名证书的HTTPs站点。
对于A类站点,可借助Android系统自带的证书校验机制,开发者无需自己实现任何代码;对于B类站点,则需要把证书文件内置到apk中,根据证书keystore得到用于校验证书内容的TrustManager,并设置在SSLContext当中。
使用HttpsURLConnection或HttpClient进行HTTPs通信,需要遵循如下安全规范:
1) 访问A类站点时,不要自定义X509TrustManager;
2) 访问B类站点时,把证书文件打包到apk中,并根据证书的keystore生成TrustManager。
这里给出使用HttpsURLConnection访问A类站点时的示例代码。

SSLContext localSSLContext = SSLContext.getInstance("TLS");
// 不要自定义X509TrustManager
localSSLContext.init(null, null, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(localSSLContext.getSocketFactory());
HttpsURLConnection localHttpsURLConnection = (HttpsURLConnection) new
URL(paramString).openConnection();
这里给出使用HttpsURLConnection访问B类站点时的示例代码。
// 根据证书文件生成keystore
private KeyStore certTrusted(Context context) throws Exception {// 从资源文件中获取.cer证书文件AssetManager am = context.getAssets();InputStream ins = am.open("12306.cer");try {// 读取证书CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");java.security.cert.Certificate cer = cerFactory.generateCertificate(ins);// 创建一个证书库,并将证书导入证书库KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");keyStore.load(null, null);keyStore.setCertificateEntry("12306", (java.security.cert.Certificate) cer);return keyStore;} finally {ins.close();}
}
……
// 获取keystore
KeyStore keystore = certTrusted(this);
SSLContext sc = SSLContext.getInstance("TLS");
// 根据keystore初始化TrustManagerFactory
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(keystore);
// 得到设置好的TrustManager,初始化SSLContext
sc.init(null, tmf.getTrustManagers(), new SecureRandom());
// 注意这里的SSLSocketFactory是javax.net.ssl包中的
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection conn = (HttpsURLConnection) new URL(paramString).openConnection();
……

使用HttpClient与HttpsURLConnection略有不同,简单来说,HttpClient中使用到的SSLSocketFactory是org.apache.http.conn.ssl包中的,并非java原生的。
下面给出使用HttpClient连接A类站点的示例代码。
首先,实现MySSLSocketFactory类:

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
// 注意这里的SSLSocketFactory是org.apache.http.conn.ssl包中的
import org.apache.http.conn.ssl.SSLSocketFactory;public class MySSLSocketFactory extends SSLSocketFactory {SSLContext sslContext = SSLContext.getInstance("TLS");public MySSLSocketFactory(KeyStore truststore) throws
NoSuchAlgorithmException,KeyManagementException,
KeyStoreException, UnrecoverableKeyException {super(truststore);sslContext.init(null, null, new SecureRandom());}@Overridepublic Socket createSocket(Socket socket, String host, int port, boolean autoClose)throws IOException, UnknownHostException {return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);}@Overridepublic Socket createSocket() throws IOException {return sslContext.getSocketFactory().createSocket();}
}

然后,再实现MyHttpClient类:

import java.security.KeyStore;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;public class MyHttpsClient {private static final int SET_CONNECTION_TIMEOUT = 50 * 1000;private static final int SET_SOCKET_TIMEOUT = 200 * 1000;public static HttpClient getNewHttpClient() {try {// 获取系统默认的KeyStore对象KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());// 使用系统默认的KeyStore对象初始化SSLSocketFactory// 注意这里的SSLSocketFactory是org.apache.http.conn.ssl中的SSLSocketFactory sf = new MySSLSocketFactory(trustStore);HttpParams params = new BasicHttpParams();HttpConnectionParams.setConnectionTimeout(params, 60000);HttpConnectionParams.setSoTimeout(params, 60000);HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);SchemeRegistry registry = new SchemeRegistry();registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));// 注意为https协议绑定之前定义的SSLSocketFactory对象registry.register(new Scheme("https", sf, 443));ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);HttpConnectionParams.setConnectionTimeout(params,SET_CONNECTION_TIMEOUT);HttpConnectionParams.setSoTimeout(params, SET_SOCKET_TIMEOUT);HttpClient client = new DefaultHttpClient(ccm, params);return client;} catch (Exception e) {return new DefaultHttpClient();}}
}

最后,使用下面的代码访问A类站点:

HttpClient hc = MyHttpsClient.getNewHttpClient();
HttpGet hg = new HttpGet(httpsURL);
HttpResponse response = hc.execute(hg);

最后,使用下面的代码访问B类站点:

// 根据证书文件生成keystore
private KeyStore certTrusted(Context context) throws Exception {// 从资源文件中获取.cer证书文件AssetManager am = context.getAssets();InputStream ins = am.open("12306.cer");try {// 读取证书CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");java.security.cert.Certificate cer = cerFactory.generateCertificate(ins);// 创建一个证书库,并将证书导入证书库KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");keyStore.load(null, null);keyStore.setCertificateEntry("12306", (java.security.cert.Certificate) cer);return keyStore;} finally {ins.close();}
}
……
// 获取keystore
KeyStore keystore = certTrusted(this);
// keystore对象作为getNewHttpClient的参数
HttpClient hc = MyHttpsClient.getNewHttpClient(keystore);
HttpGet hg = new HttpGet(httpsURL);
HttpResponse response = hc.execute(hg);
……

3. HostnameVerifier

HostnameVerifier用于实现HTTPs通信中的域名安全校验,即验证当前连接的HTTPs站点的SSL证书中的域名是否等于站点本身的域名。若使用不当,将导致APP对域名不作校验,从而使黑客有了中间人攻击的可乘之机。开发者经常犯的错误有如下:
 自定义HostnameVerifier,且不做任何校验逻辑,一般为return true;
 使用Android系统中自带的不安全的HostnameVerifier,效果等同于不做任何校验逻辑:
 org.apache.http.conn.ssl.AllowAllHostnameVerifier
 org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
这里给出常见的自定义HostnameVerifier的错误示例代码(以使用HttpsURLConnection进行HTTPs连接的情况为例)。

// 自定义HostnameVerifier
HttpsURLConnection.setDefaultHostnameVerifier(new MyHostnameVerifier());
HttpsURLConnection localHttpsURLConnection = (HttpsURLConnection) new
URL(paramString).openConnection();
……
class MyHostnameVerifier implements HostnameVerifier {@Override// 不作任何校验public boolean verify(String arg0, SSLSession arg1) {return true;}
}

这里给出使用Android系统中自带的不安全的HostnameVerifier的错误示例代码(以使用HttpsURLConnection进行HTTPs连接的情况为例)。

URL url = new URL("https://www.example.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
InputStream in = urlConnection.getInputStream();

使用上述方式进行HTTPs通信,将存在中间人攻击的可能。由于缺乏域名校验机制,黑客可以通过使用自签名证书,结合DNS欺骗,使用户访问恶意页面。因此,使用HttpsURLConnection或HttpClient进行HTTPs通信时,需要验证域名的合法性。
使用HttpsURLConnection或HttpClient进行HTTPs通信,需要遵循如下安全规范:
1) 不要自定义HostnameVerifier;
2) 不要使用如下Android系统中自带的不安全HostnameVerifier:
 org.apache.http.conn.ssl.AllowAllHostnameVerifier
 org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
3) 使用Android系统中自带的安全HostnameVerifier:
 org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER
这里给出安全的HostnameVerifier的示例代码。

URL url = new URL("https://www.example.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
InputStream in = urlConnection.getInputStream();

Https证书校验不当引起的安全问题相关推荐

  1. 在okhttp3,WebView中忽略HTTPS证书校验

    在APP开发过程中,后台使用的可能是自签的Https证书,如果不忽略证书校验,会出现Trust anchor for certification path not found的错误 Okhttp3忽略 ...

  2. 移动端HTTPS证书校验过程是怎样的

    写了几篇关于HTTPS证书的文章之后,让我对HTTPS证书的内容以及证书的申请下载等问题,有了一定的了解.今天在这篇里咱再了解一下关于移动端HTTPS证书校验的相关问题,HTTPS证书购买和HTTPS ...

  3. 基于Android10的忽略HTTPS证书校验

    文章目录 为什么要忽略证书校验 证书校验不通过,怎么办呢? 为什么要忽略证书校验 从Android 9 开始 APP默认访问的URL 必须是HTTPS协议的,虽然可以配置回支持HTTP,但这种做法不建 ...

  4. android中webView的https证书校验以及基于okhttp的接口https证书校验

    webView证书校验: 通过chrome浏览器拿到证书cer文件 获取证书公钥 public void readX509CerFile() {try {InputStream inStream = ...

  5. [免费专栏] Android安全之绕过直连、HOST校验、系统证书校验、代理检测、双向认证抓HTTPS数据

    也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...

  6. iOS https证书双向认证的实现机制

    文章目录 原理 单向认证流程 双向认证流程 证书生成 生成自签名根证书 生成自签名服务器端证书 生成自签名客户端证书 AFNetworking对于证书的校验机制 原理 双向认证,顾名思义,客户端和服务 ...

  7. golang http client跳过安全证书校验

    // 跳过https证书校验tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true},} ...

  8. 利用Frida手动绕过Android-APP证书校验

    HTTPS证书校验绕过有很多成熟的方法,比如SSL Unpinning,JustTrustMe等,但是遇到混淆过或写在so里的校验的时候是无效的. 本文写的可能会有些啰嗦,记录了我整个尝试的思路.没有 ...

  9. 解决ImageLoader加载HTTPS图片证书校验异常问题

    解决ImageLoader加载HTTPS图片证书校验异常问题 参考文章: (1)解决ImageLoader加载HTTPS图片证书校验异常问题 (2)https://www.cnblogs.com/cs ...

最新文章

  1. python图像相似度识别_一个用SIFT特征比较图像相似度的python小程序
  2. 设计模式6+1大原则
  3. linux创建 / 删除用户及用户管理
  4. matlab 判断鼠标按下,Matlab:如何通过使用回调来获取当前鼠标在点击位置
  5. 势能线段树(均摊分析)
  6. hdu 4296 Buildings (贪心)
  7. 计算机网络系统是弱电工程,弱电工程包括哪些类型?常见的问题又有哪些?
  8. L2-007 家庭房产 (25 point(s))
  9. python语言程序设计实践教程实验八答案_Python程序设计实践教程
  10. C语言程序设计C语言之父,C程序设计语言-美-里奇-C语言之父-机械工业出版社
  11. 微服务架构下的统一身份认证和授权
  12. RISC-V嵌入式开发入门篇2:RISC-V汇编语言程序设计(上)
  13. 使用CSS3实现按钮特效
  14. Shorten the command line via JAR manifest or via a classpath file and rerun.
  15. 怎样查看主板支持服务器内存,怎么看主板支持内存频率?看主板支持内存频率方法...
  16. 中国首届DFMA降本设计峰会
  17. php 生成微信小程序码(可携带参数) 永久有效,数量暂无限制
  18. 公司-饿了么:饿了么
  19. 邮件代发、国外邮件代发、外贸邮件代发平台,送达率、打开率高的秘密
  20. 【《深入理解计算机系统》读书笔记(3)】 第3章 程序的机器级表示

热门文章

  1. #笔记-面向对象基础知识
  2. 东华oj-进阶题第87题-挤牛奶
  3. BZOJ 2216 Poi2011 Lightning Conductor 动态规划
  4. EMVTag系列8《IC卡公钥证书》
  5. Java 虚拟机(JVM)运行时内存结构
  6. Mybatis学习笔记(三)
  7. 《数据结构与算法》(十四)- 图的应用:最短路径
  8. CSP 202112-3 登机牌条码 40分
  9. java刷新透视表数据源,Java 创建、刷新Excel透视表/设置透视表行折叠、展开
  10. Python基础之控制结构