微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

官方的Oracle SSLSocketClient.java演示代码是否不安全?

如何解决官方的Oracle SSLSocketClient.java演示代码是否不安全?

从此link,给出SSLSocketClient.java的演示:

import java.net.*;
import java.io.*;
import javax.net.ssl.*;

/*
 * This example demostrates how to use a SSLSocket as client to
 * send a HTTP request and get response from an HTTPS server.
 * It assumes that the client is not behind a firewall
 */

public class SSLSocketClient {

    public static void main(String[] args) throws Exception {
        try {
            SSLSocketFactory factory =
                (SSLSocketFactory)SSLSocketFactory.getDefault();
            SSLSocket socket =
                (SSLSocket)factory.createSocket("www.verisign.com",443);

            /*
             * send http request
             *
             * Before any application data is sent or received,the
             * SSL socket will do SSL handshaking first to set up
             * the security attributes.
             *
             * SSL handshaking can be initiated by either flushing data
             * down the pipe,or by starting the handshaking by hand.
             *
             * Handshaking is started manually in this example because
             * PrintWriter catches all IOExceptions (including
             * SSLExceptions),sets an internal error flag,and then
             * returns without rethrowing the exception.
             *
             * Unfortunately,this means any error messages are lost,* which caused lots of confusion for others using this
             * code.  The only way to tell there was an error is to call
             * PrintWriter.checkerror().
             */
            socket.startHandshake();

            PrintWriter out = new PrintWriter(
                                  new BufferedWriter(
                                  new OutputStreamWriter(
                                  socket.getoutputStream())));

            out.println("GET / HTTP/1.0");
            out.println();
            out.flush();

            /*
             * Make sure there were no surprises
             */
            if (out.checkerror())
                System.out.println(
                    "SSLSocketClient:  java.io.PrintWriter error");

            /* read response */
            BufferedReader in = new BufferedReader(
                                    new InputStreamReader(
                                    socket.getInputStream()));

            String inputLine;
            while ((inputLine = in.readLine()) != null)
                System.out.println(inputLine);

            in.close();
            out.close();
            socket.close();

        } catch (Exception e) {
            e.printstacktrace();
        }
    }
}

我有两个问题:

  1. 根据this official document,如果我们使用的是原始SSLSocketFactory而不是HttpsURLConnection,则握手过程中不会强制执行主机名验证。因此,主机名验证应手动完成。

使用原始SSLSocket和SSLEngine类时,应始终在发送任何数据之前检查对等方的凭据。 SSLSocket和SSLEngine类不会自动验证URL中的主机名是否与对等方凭据中的主机名匹配。如果未验证主机名,则可以通过URL欺骗利用应用程序。从JDK 7开始,可以在SSL / TLS握手期间处理端点标识/验证过程。请参阅SSLParameters.getEndpointIdentificationAlgorithm方法

这是否意味着演示不安全?

  1. 我看到了在Java 7中添加主机名验证的解决方案,如下所示:

    SSLParameters sslParams = new SSLParameters(); sslParams.setEndpointIdentificationAlgorithm(“ HTTPS”); sslSocket.setSSLParameters(sslParams);

当算法指定为“ HTTPS”时,握手将验证主机名。否则(仅使用原始SSLSockeFactory的算法为空),根本未调用主机名验证。 我很好奇我可以解决如下问题:

SSLSocketFactory factory =
                    (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket =
                    (SSLSocket)factory.createSocket("www.verisign.com",443);
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
if(!hv.verify(socket.getSession().getPeerHost(),socket.getSession())){
    threw CertificateException("Hostname does not match!")
}

我看到HttpsURLConnection.getDefaultHostnameVerifier()可以返回认的HostnameVerifier,我可以用它来进行验证吗?我看到很多人在谈论使用自定义HostnameVerifier。我不知道是否存在认值,为什么我们需要自定义它?

解决方法

边界线作为答案,但是评论太长了。

(1)是,对于HTTPS (如您所引用的内容后面的段落中所述),这是一个安全漏洞;此示例可能是在Java 7之前编写的,此后未进行更新。您可以提交错误报告,让他们进行更新。 (当然,有些使用SSL / TLS应用程序验证主机名,例如SNMPS和LDAPS,甚至没有 URL,但仍可以实现使用Java JSSE。)

(2)HTTP也错误或不正确:

  • PrintWriter使用的JVM的lineSeparator随平台而异,但是HTTP标准(RFC 2068、2616、7230)在所有平台上均要求CRLF作为请求标头,尽管某些服务器(可能包括google)仅接受-LF遵循传统的Postel格言:“在发送邮件时保持保守,在接收邮件时保持自由”;

  • 读取端假定所有数据都是面向行的,并且不会因规范化EOL而损坏,对于HTTP header some 主体(例如当请求没有接受(或接受编码)但不能保证时,您将从大多数 Web服务器获取的文本/ html;

  • 读取端还假设可以安全地从JVM默认“字符集”解码所有数据并将其重新编码为;这对于HTTP标头(实际上是7位ASCII)是正确的,但不是很多/大多数主体:特别是处理8859或类似UTF8的东西会破坏掉其中的大部分,而处理UTF8像8859或CP1252则会把它弄乱。

(3)尽管HTTP / 1.0仍然得到广泛支持并且使演示变得更加简单,但它正式被淘汰了,所以让我放一张幻灯片。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。