共计 2500 个字符,预计需要花费 7 分钟才能阅读完成。
https 协议就是 http+ssl 协议,如下图所示为其连接过程:
客户端向服务端发送 https 请求;
服务端收到请求之后,生成公钥和私钥。公钥相当于是锁,私钥相当于是钥匙,只有私钥才能够打开公钥锁住的内容;
服务端将公钥(证书)返回给客户端,公钥里面包含有很多信息,比如证书的颁发机构、过期时间等等;
客户端收到公钥之后,首先会验证其是否有效,如颁发机构或者过期时间等,如果发现有问题就会抛出异常,提示证书存在问题。如果没有问题,那么就生成一个随机值,作为客户端的密钥,然后用服务端的公钥加密;
客户端用服务端的公钥加密密钥,然后发送给服务端。
服务端收到经过加密的密钥,然后用私钥将其解密,得到客户端的密钥,然后服务端把要传输的内容和客户端的密钥进行对称加密,这样除非知道密钥,否则无法知道传输的内容。
服务端将经过加密的内容传输给客户端。
客户端获取加密内容后,用之前生成的密钥对其进行解密,获取到内容。
https 也不是绝对安全的,如下图所示为中间人劫持攻击,中间人可以获取到客户端与服务器之间所有的通信内容。
中间人截取客户端发送给服务器的请求,然后伪装成客户端与服务器进行通信;将服务器返回给客户端的内容发送给客户端,伪装成服务器与客户端进行通信。
通过这样的手段,便可以获取客户端和服务器之间通信的所有内容。
使用中间人攻击手段,必须要让客户端信任中间人的证书,如果客户端不信任,则这种攻击手段也无法发挥作用。
造成中间人劫持的原因是 没有对服务端证书及域名做校验或者校验不完整,为了方便,直接采用开源框架默认的校验方式进行 https 请求
如 volley
OKhttp3.0
预防方法:
预防方式有两种
1、针对安全性要求比较高的 app,可采取客户端预埋证书的方式锁死证书,只有当客户端证书和服务端的证书完全一致的情况下才允许通信,如一些银行类的 app,但这种方式面临一个问题,证书过期的问题,因证书有一定的有效期,当预埋证书过期了,只有通过强制更新或者要求用户下载证书来解决。
以 volley 为例:校验的实现方式如下
通过预埋证书创建 SSLSocketFactory;
private static SSLSocketFactory buildSSLSocketFactory(Context context,
int certRawResId) {
KeyStore keyStore = null;
try {keyStore = buildKeyStore(context, certRawResId);
} catch (KeyStoreException e) {e.printStackTrace();
} catch (CertificateException e) {e.printStackTrace();
} catch (NoSuchAlgorithmException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();
}
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = null;
try {tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
} catch (NoSuchAlgorithmException e) {e.printStackTrace();
} catch (KeyStoreException e) {e.printStackTrace();
}
SSLContext sslContext = null;
try {sslContext = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {e.printStackTrace();
}
try {sslContext.init(null, tmf.getTrustManagers(), null);
} catch (KeyManagementException e) {e.printStackTrace();
}
return sslContext.getSocketFactory();
生成 经过 ssl 校验及 域名校验的 connection
2 针对安全性要求一般的 app,可采用通过校验域名,证书有效性、证书关键信息及证书链的方式
以 volley 为例,重写 HTTPSTrustManager 中的 checkServerTrusted 方法,同时开启域名强校验
目前很多应用都用 webview 加载 H5 页面,如果服务端采用的是可信 CA 颁发的证书,在 webView.setWebViewClient(webviewClient) 时重载 WebViewClient 的 onReceivedSslError(),如果出现证书错误,直接调用 handler.proceed() 会忽略错误继续加载证书有问题的页面,如果调用 handler.cancel() 可以终止加载证书有问题的页面,证书出现问题了,可以提示用户风险,让用户选择加载与否,如果是需要安全级别比较高,可以直接终止页面加载,提示用户网络环境有风险:
不建议直接用 handler.proceed()。如果 webview 加载 https 需要强校验服务端证书,可以在 onPageStarted() 中用 HttpsURLConnection 强校验证书的方式来校验服务端证书,如果校验不通过停止加载网页。当然这样会拖慢网页的加载速度,需要进一步优化,具体优化的办法不在本次讨论范围,这里也不详细讲解了。