当前位置: 首页 > 知识库问答 >
问题:

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

壤驷建德
2023-03-14

从这个链接中,给出了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. 根据该官方文档,如果我们使用的是原始SSLSocketFactory而不是HttpsURLConnection,则握手过程中没有强制执行的主机名验证。因此,主机名验证应手动进行。

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

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

我在Java 7中看到一个添加主机名验证的解决方案是:

SSLParameters sslParams=新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个答案

龚苏燕
2023-03-14

作为一个边缘的答案,但太长的评论。

(1)是的,对于HTTPS(如您所引用的那一段之后的段落所指出的),这是一个安全缺陷;这个例子可能是在Java 7之前写的,之后就没有更新过。您可以提交一个bug报告供他们更新。(当然,有些使用SSL/TLS的应用程序不验证主机名,比如SNMPS和LDAPS,甚至没有URL,但仍然可以使用Java JSSE实现。)

(2)HTTP错误或差也:

>

  • PrintWriter使用JVM的lineSeparator,它因平台而异,但HTTP标准(RFCs 2068、2616、7230)要求所有平台上的请求头都需要CRLF,尽管一些服务器(可能包括google)将按照传统的Postel格言“在发送的内容中保守,在接收的内容中自由”接受仅-LF;

    读取端假设所有数据都是面向行的,并且不会因为规范化EOL而受损,这对于HTTP头和一些主体是正确的,比如当请求没有接受(或接受编码)时,您将从大多数Web服务器获得的text/html(但不保证);

    读取端还假设所有数据都可以安全地从JVM缺省的“charset”解码并重新编码为该缺省的“charset”;对于HTTP头(实际上是7位ASCII)是这样的,但不是很多/大多数主体:特别是,将8859或类似于UTF8的处理会破坏它的大部分,而将UTF8处理为8859或CP1252则会mojibrake它。

    (3)HTTP/1.0已经过时了,尽管它仍然受到广泛的支持,而且演示也要简单得多,所以我还是不提这个问题。

  •  类似资料:
    • Rust 主要魅力是它强大的静态行为保障。不过安全检查天性保守:有些程序实际上是安全的,不过编译器不能验证它是否是真的。为了写这种类型的程序,我们需要告诉编译器稍微放松它的限制。为此,Rust 有一个关键字,unsafe。使用unsafe的代码比正常代码有更少的限制。 让我们过一遍语法,接着我们讨论语义。unsafe用在两个上下文中。第一个标记一个函数为不安全的: unsafe fn danger

    • 当C#被unsafe修饰符标记时,C#允许在代码块的函数中使用指针变量。 unsafe code或非托管代码是使用pointer变量的代码块。 Note - 要在codingground执行本章中提到的程序,请在Project 》》 Compile Options 》》 Compilation Command设置编译选项。 mcs *.cs -out:main.exe -unsafe" Point

    • 当代码段被 unsafe 修饰符标记时,C# 允许该代码段中的函数使用指针变量,故使用了指针变量的代码块又被称为不安全代码或非托管代码。 注意: 若要在 codingground 中执行本章的程序,请将 Project >> Compile Options >> Compilation Command to 中的编辑项设置为 mcs *.cs -out:main.exe -unsafe” 指针 指

    • 问题内容: 除了迷惑之外,还有其他方法可以防止罐子被其他人打开吗?问题是我不希望任何人访问代码,这就是为什么我不喜欢Java。从我使用的反编译器来看,用C#和Java编写的程序具有完好无损的功能,就像变量名一样,这使得访问非免费程序变得容易。更糟糕的是,给出源代码。 问题答案: 上面的评论涵盖了大多数这些要点,但是我将在这里对其进行扩展: 如果您的代码在用户的计算机上运行,​​则用户可以反编译您的

    • 本文中的提示和技巧有些是针对网络服务器的建立的,有些是综合性的,其余的则是针对Apache的。 保持不断更新和升级 Apache HTTP服务器有一个很好的安全记录和一个高度关注安全问题的开发社团。但是这仍然不能避免在发行版中存在或大或小的问题。所以知道这个软件的版本更新和升级补丁是至关重要的。如果你是直接从Apache组织得到Apache HTTP服务器的,我们强烈建议你订阅Apache HTT

    • 黑客有没有办法抓住这个按钮并“点击”它? 是否需要添加另一层安全性?