漏洞说明
/**
* 覆盖java默认的证书验证
*/
private static final TrustManager[] TRUSTALLCERTS = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
@SuppressLint("TrustAllX509TrustManager")
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@SuppressLint("TrustAllX509TrustManager")
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
}
};
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, TRUSTALLCERTS, new java.security.SecureRandom()); //配置自己的信任管理类(信任策略)
SSLSocketFactory newFactory = sc.getSocketFactory();
connection.setSSLSocketFactory(newFactory);
} catch (Exception e) {
e.printStackTrace();
}
如上代码所示,自定义与使用了X509TrustManager子类,但又没有实现信任校验的逻辑时遇,对安全平台扫描APK时,会报如下风险
【漏洞】自定义实现的X509TrustManager子类中,未对服务器端证书做验证,默认接受任意服务端证书,会存在安全风险,可能会导致恶意程序利用中间人攻击绕过证书校验
【修复的建议通常的如下】
利用X509TrustManager子类中的checkServerTrusted函数,校验服务器端证书的合法性
PS:一般自定义信任管理器,通常是使用自签名或android不认可的证书颁发机构SSL证书的业务场景
使用这个方式最麻烦是公钥/证书放置在前端,以及公钥的同步等问题!!!
修复建议
个人的修复建议是
【前提条件】
1、你的SSL证书是由android认可的证书颁发机构或者该结构下属的机构颁发的证书(最关键)
2、使用的https的服务的请求与响应的数据安全性不是那么高(数据不敏感与私密),
3、又不保证服务器的证书是否不会被替换,
那么性价较高且简单明了的方案是
不要使用509TrustManager子类!
不要使用509TrustManager子类!
不要使用509TrustManager子类!
即使用默认的系统网络库默认的信任管理/校验方案,系统会使用证书链的信任管理策略
PS:
Android已经把将近150个CA根证书(数字证书认证机构认证过的证书)内置在我们手机中。这150多个证书被全世界信赖
TIPS分享
查看域名的证书的信息
利用chrome打开对应的域名,查看即可
证书详情
查看信息的信证CA根证书
设置->安全->信任的凭据
自定义X509TrustManage示例
说明 : 如下代码copy自 学会正确的使用 https,不要留下漏洞!!
public static SSLSocketFactory getSSLSocketFactory() throws Exception {
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
// 本地公钥信息,实现项目中在考虑该值的更新,否则服务证书一换,之前的端不会能正常工作了
public static final String PUB_KEY = "3082010a0282010100d52ff5dd432b3a05113ec1a7065fa5a80308810e4e181cf14f7598c8d553cccb7d5111fdcdb55f6ee84fc92cd594adc1245a9c4cd41cbe407a919c5b4d4a37a012f8834df8cfe947c490464602fc05c18960374198336ba1c2e56d2e984bdfb8683610520e417a1a9a5053a10457355cf45878612f04bb134e3d670cf96c6e598fd0c693308fe3d084a0a91692bbd9722f05852f507d910b782db4ab13a92a7df814ee4304dccdad1b766bb671b6f8de578b7f27e76a2000d8d9e6b429d4fef8ffaa4e8037e167a2ce48752f1435f08923ed7e2dafef52ff30fef9ab66fdb556a82b257443ba30a93fda7a0af20418aa0b45403a2f829ea6e4b8ddbb9987f1bf0203010001";
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
if (chain == null) {
throw new IllegalArgumentException("checkServerTrusted:x509Certificate array isnull");
}
if (!(chain.length > 0)) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
}
if (!(null != authType && authType.equalsIgnoreCase("RSA"))) {
throw new CertificateException("checkServerTrusted: AuthType is not RSA");
}
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init((KeyStore) null);
for (TrustManager trustManager : tmf.getTrustManagers()) {
((X509TrustManager) trustManager).checkServerTrusted(chain, authType);
}
} catch (Exception e) {
throw new CertificateException(e);
}
RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();
String encoded = new BigInteger(1 , pubkey.getEncoded()).toString(16);
final boolean expected = PUB_KEY.equalsIgnoreCase(encoded);
if (!expected) {
throw new CertificateException("checkServerTrusted: Expected public key: "
+ PUB_KEY + ", got public key:" + encoded);
}
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}};
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts,
new java.security.SecureRandom());
return sslContext
.getSocketFactory();
}