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. 网络模型mAP计算实现代码
  2. php wordpress 开源,PHP 遭弃用!WordPress.com 开源并转用 Javascript
  3. java实现扫地agent_如何实现java agent?分享java agent的使用案例
  4. GET 和 POST 的区别(重要,面试常问)
  5. java digestutils_java-Apache DigestUtils似乎部分错误地计算了MD5
  6. webservice 启用代理服务器
  7. 美军开发远程人脸识别系统,实现1公里内目标识别
  8. python的进程和线程_Python进程与线程知识
  9. python具有可嵌入性_如何构建可嵌入Python
  10. 拓端tecdat|R语言中的生存分析Survival analysis晚期肺癌患者4例
  11. 计算机房设计规范2008,电子信息系统机房设计规范(GB50174-2008)(下)
  12. 在Ubuntu中为ROG笔记本安装驱动asusctl
  13. 用参数方程绘制椭球体
  14. Android APK瘦身优化
  15. 虚幻引擎4艺术大师 - 中文版免费分享
  16. python sasl_python用sasl的方式连接ldap提示
  17. 精美UI静态界面欣赏
  18. Kafka bootstrap.servers
  19. EtcGame华丽升级为Coingame 新增ETH投注!
  20. android 分享二维码图片到微信QQ(url地址字符串生成二维码图片、分享二维码图片到微信QQ)

热门文章

  1. 计算机培训中心规章制度,某计算机培训中心规章制度.doc
  2. Oracle 查询一个月内每天指定时间段内的数据量
  3. 生鲜配送系统有哪些功能?搭建生鲜配送系统有什么优势?
  4. P2P网贷平台系统市场占有率排行榜
  5. PyQt5 从零开始环境搭建
  6. Android Studio 用USB连接到真机调试方法
  7. PaddleOCR实践之飞桨常规赛:中文场景文字识别
  8. 能够在乱世中_东夏国,为何在乱世中还可以立国?是有什么原因呢?
  9. 51单片机学习笔记之新建工程、点亮一盏小灯
  10. in 在将 nvarchar 值转换成数据类型 int 时失败