我正在寻找类似于this answer的解决方案,但更安全。我想禁用证书验证,但仅针对单个请求(这是我所需要的)。所以它应该做以下一个或多个
当我要求更安全的解决方案时,我真的很想知道这个问题(得分-2)与原始问题(得分+46)相比有什么问题。有人可以解释一下吗?
解释为什么我需要这个:有一个有效的服务器证书,通常,它会被使用。但我需要向localhost
发送一个请求,它也必须在开发人员计算机上运行。它必须是https
,因为没有http
支持。
答案 0 :(得分:7)
此实用程序类基于提供的示例。使用“宽松”SSLContext,预先配置TrustManager接受所有证书。如果您的localhost ssl证书颁发给其他CN
,还需要主机名验证程序将其应用于需要使用HttpsURLConnection.setSocketFactory
和HttpsURLConnection.setHostnameVerifier
进行“放松”的ssl验证的每个连接。默认行为不会改变
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class RelaxedSSLContext {
// Create a trust manager that does not validate certificate chains like
public static TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers()
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
//No need to implement.
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
//No need to implement.
}
}
};
//hostname verifier. All hosts valid
public static HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
//hostname verifier. Only localhost and 127.0.0.1 valid
public static HostnameVerifier localhostValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return "localhost".equals(hostname) || "127.0.0.1".equals(hostname);
}
};
public static SSLContext getInstance() throws KeyManagementException, NoSuchAlgorithmException{
return getInstance("SSL");
}
//get a 'Relaxed' SSLContext with no trust store (all certificates are valids)
public static SSLContext getInstance(String protocol) throws KeyManagementException, NoSuchAlgorithmException{
SSLContext sc = SSLContext.getInstance(protocol);
sc.init(null, trustAllCerts, new java.security.SecureRandom());
return sc;
}
}
以这种方式使用
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(RelaxedSSLContext.getInstance().getSocketFactory());
conn.setHostnameVerifier(RelaxedSSLContext.localhostValid);
示例1(默认)
url = "https://www.google.cop/finance";
conn = url.openConnection();
conn.connect();
int statusCode = conn.getResponseCode(); // 200
示例2(错误的主机名)
url = "https://216.58.210.164/finance";
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(RelaxedSSLContext.allHostsValid);
conn.connect();
int statusCode = conn.getResponseCode(); //200
// ERROR 'No subject alternative names matching IP address 216.58.210.164 found' without hostnameVerifier
示例3(链不在默认信任库中)
url = "https://www.aragon.es";
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(RelaxedSSLContext.getInstance().getSocketFactory());
conn.connect();
int statusCode = conn.getResponseCode(); //200
// ERROR 'unable to find valid certification path to requested target' without relaxedSocketFactory
我认为你的问题是关于主题的,有用的并且表达得很好。在某些情况下,应用高安全性的上下文是合适的,或者更确切地说,并不是必需的。普通HTTP仍在使用......
让我们分析一下你的背景。必需的是通过HTTPS访问localhost而不信任服务器标识。这意味着您要接受服务器提供的任何证书。此场景的安全问题是MITM附加(中间的人)。来自(维基百科)
攻击者秘密传递并可能改变之间的通信 两方认为他们正在与每个人直接沟通 其他
但是,对于localhost 的不受信任的https连接是否可能发生 MITM攻击?
首先,在https中,服务器必须向客户端提供服务器证书。客户端验证证书的公钥并检查其是否与本地信任库匹配。 通过网络管理员的协作,可以嗅探网络并设置代理来拦截连接。但是恶意代理并不拥有匹配的私钥,因此代理服务器可能会尝试伪造证书并提供自己的公钥。 证书不会出现在客户端信任中,因此连接将被拒绝。但是,如果删除信任库验证,理论上可以进行攻击。
但是,是MITM可能限制与localhost的连接吗?
请参阅https://security.stackexchange.com/questions/61399/are-mitm-attacks-possible-on-http-loopbacks
在一般情况下,这是不可能的,但是对该计算机具有root访问权限的攻击者可以更改DNS并将请求重定向到恶意代理。即使使用127.0.0.1,如果应用程序有办法配置连接端口也是可能的。
偏执的解决方案可能是硬编码服务器连接URL,端口甚至是trustore。但是你在开发环境中谈论localhost,所以我认为我们可以放松一点
答案 1 :(得分:2)
只需使用实例方法setX()
而不是静态setDefaultX()
:
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(...);
connection.setHostnameVerifier(...);
答案 2 :(得分:0)
解决方法始终是将服务器证书导入客户端信任库,否则,最好是获取受信任CA签名的服务器证书(如果它在您的控制之下)。你不能在代码中安全地做到这一点,你不应该只是“只为了开发”。否则,您不会在开发中测试生产代码。