充分验证SSL/TLS
许多应用程序没有正确验证SSL / TLS证书,使其易受中间人(MITM)攻击。如果应用程序无法正确验证其与服务器的连接,则该应用程序易受特权网络攻击者的MITM攻击。这种类型的攻击使得劫持者能够捕获,查看和修改在应用程序和服务器之间发送和接收的流量。
细节
没有正确验证其与服务器的连接的应用程序容易受到特权网络攻击者的中间人攻击。这意味着攻击者将能够捕获,查看和修改在应用程序和服务器之间发送和接收的流量。
常见错误:接受自签名证书
由于各种原因,开发人员可能会在应用程序中禁用证书验证。一个例子是开发人员需要测试生产服务器上的代码,但没有测试环境的域证书。在这种情况下,开发人员可能会设置接受所有证书为有效。然而,接受所有证书是有效的,将允许攻击者通过简单地使用自签名证书对应用程序执行MITM攻击。
这种设置会使SSL/TLS失去作用,并且提供了相当于未加密的明文连接(除了需要活动的MITM攻击以查看和修改流量,还可以被动地监视明文连接)。
以下是允许所有SSL / TLS证书有效的易受攻击的Android代码的示例:
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
};
//Globally set the broken TrustManager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
//Make the connection to the server
URL url = new URL("https://paypal.com");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
InputStream ins = urlConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(ins);
BufferedReader in = new BufferedReader(isr);
String inputLine;
in.close();
常见错误:设置主机名白名单
实施SSL / TLS的另一个常见的开发错误是设置一个主机名验证程序。在这种情况下,该应用程序将不会接受自签名证书,因为证书仍然被验证。但是,如果一个应用程序“允许所有主机名”,任何有效的证书颁发机构(CA)为任何域名颁发的证书可用于执行MITM攻击并签署流量。
以下是一个易受攻击的Android代码的例子,它设置了一个主机名验证白名单:
URL url = new URL("https://paypal.com");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
InputStream ins = urlConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(ins);
BufferedReader in = new BufferedReader(isr);
String inputLine;
in.close();
修复
通用建议
对于处理高度敏感数据的应用程序,请使用证书锁定来防止MITM攻击。大多数应用程序已经定义了它们需要连接的后台服务器,并且默认信任他们连接的基础设施,因此使用与公共证书不同的“私有”公共密钥基础架构是可以被接受的(通常会更安全)。在这种环境下,攻击者在无法接触物理设备的情况下需要获取服务器端的私钥进行MITM攻击。
如果针对处理高度敏感数据的应用程序功能无法实现证书锁定,那么请执行正确的证书验证。其中包括两部分:
- 证书验证:提交给应用程序的证书必须由应用程序完全验证,并由受信任的根CA签名。
- 主机名验证:应用程序必须检查并验证从证书中提取的主机名(通用名称或CN)是否与应用程序打算通信的主机名相同。
对于Android
将证书锁定到Android附带的默认Apache HTTP客户端下,包括获取所需主机的证书,以.bks格式转换证书,然后将该证书锁定为DefaultHttpClient的实例。BKS密钥库通常包含在应用的APK文件的资源/原始目录中。
以下示例代码演示如何加载BKS密钥库:
InputStream in = resources.openRawResource(certificateRawResource);
keyStore = KeyStore.getInstance("BKS");
keyStore.load(resourceStream, password);
构造的httpClient实例可以被配置为仅允许请求托管已经使用存储在应用程序内的证书签署的证书。
以下示例代码说明了这种方法:
HttpParams httpParams = new BasicHttpParams();
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("https", new SSLSocketFactory(keyStore), 443));
ThreadSafeClientConnManager clientMan = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
httpClient = new DefaultHttpClient(clientMan, httpParams);
有关在Android中实施证书锁定的更多信息,请参阅OWASP证书和公钥引导指南https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#Android。
此外,CWAC-NetSecurity是一个库,可以将Android 7.0网络安全配置子系统回退到Android 4.2,从而更轻松地将应用绑定到特定的证书颁发机构或证书,支持自签名证书,并支持处理其他高级SSL证书方案。
对于iOS
一个选择是使用NSURLSession或AFNetworking类来实现iOS中的证书锁定。有关此实现的其他详细信息,请参见https://developer.apple.com/library/ios/technotes/tn2232/_index.html上的技术说明“HTTPS服务器信任评估”。
此外,TrustKit是一个开源框架,可以帮助开发人员更轻松地在iOS中部署公钥密码。
参考
- Your app shouldn’t suffer SSL’s problems - https://moxie.org/blog/authenticity-is-broken-in-ssl-but-your-app-ha/
- OWASP Mobile Top 10: M3- Insufficient Transport Layer Protection
- CWE: CWE-319 - Cleartext Transmission of Sensitive Information