Re: JDBC SSL hostname verification

From: Bruno Harbulot <bruno(at)distributedmatter(dot)net>
To: pgsql-jdbc(at)postgresql(dot)org
Subject: Re: JDBC SSL hostname verification
Date: 2011-08-06 15:30:27
Message-ID: j1jmmg$f06$1@dough.gmane.org
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-jdbc

On 06/08/2011 13:02, Craig Ringer wrote:
> On 6/08/2011 11:00 AM, Bruno Harbulot wrote:
>
> JSSE doesn't verify the hostname automatically. Quoting the JSSE
> reference guide for Java 6:
>
> "When using raw SSLSockets/SSLEngines you should always check the peer's
> credentials before sending any data. The SSLSocket and SSLEngine classes
> do not automatically verify that the hostname in a URL matches the
> hostname in the peer's credentials. An application could be exploited
> with URL spoofing if the hostname is not verified."
>
> I was under the impression that PgJDBC verified the hostname its self
> unless verification was disabled, but it seems not. Hmm. I guess you can
> use a custom SSLSocketFactory to do the verification, but it really
> should be something done by the stock JDBC driver. Patch?

Yes, you're absolutely right, there is no verification by default. In
fact, until the relatively recent RFC 6125 [1], the way host names
should be verified was specified differently in each protocol (e.g. RFC
2818 for HTTPS, see RFC 6125 appendix B).

This patch (attached), modifies org.postgresql.ssl.MakeSSL and changes this:
> Socket newConnection = factory.createSocket(stream.getSocket(), stream.getHost(), stream.getPort(), true);
> stream.changeSocket(newConnection);

Into this:
> SSLSocket newConnection = (SSLSocket)factory.createSocket(stream.getSocket(), stream.getHost(), stream.getPort(), true);
> // Gets the session and do the verification...
> stream.changeSocket(newConnection);

In principle, this verification could indeed be done within the custom
SSLSocketFactory.createSocket(...) method. However, this might need to
be an SSLSocketFactory intended to be used only with this JDBC driver as
it would break the general API of SSLSocketFactory.

This is because the factory would return an SSLSocket for which the TLS
handshake has already taken place (since it needs to get the peer
certificate in order to verify them). From the SSLSocketFactory API
point of view, the factory creates SSLSockets, but doesn't initiate the
handshake. The handshake is only initiated (perhaps implicitly)
afterwards, in one of the 3 conditions documented in SSLSocket [2].
A number of settings can still be made before the handshake, and it's
normally not up to the SSLSocketFactory to make them.

I've tried to follow the rules in RFC 6125 to perform the verification
(although I haven't looked at what it says about certificates for IP
addresses, only host names).

Firstly, it looks for Subject Alternative Names and then for CN entries
in the Subject Distinguished Names (Subject DN). (It is preferrable to
use subjectAltNames rather than relying on CNs in the Subject DN, see
RFC 6125 section 1.5.)
If it finds a subjectAltName entry of type DNS that matches the
requested host name, it stops and accepts the certificate.
Otherwise, it looks within the Subject DN for and RDN with only one AVA
such as CN=the.host.name (section 2.3.1 of RFC 6125).
(I must admit I think the CN verification could be done more elegantly;
in addition, I haven't looked in details into the subtleties of
character encoding.)

I understand this may not be obvious if one is not used to certificates.
The Subject DN in the certificate is a sequence of relative
distinguished names (RDNs). Each RDN is an unordered set of Attribute
Value Assertions (AVAs), for example "CN=the.host.name".
Using the RFC 2253 string representation of a DN, RDNs are separated
with ',' and AVAs are separated with '+'. (OpenSSL tends to use a
different string representation, separated with "/" and sometimes in a
different order.) They can also be escaped with '\' (which means it's
not necessarily easy to implement with a StringTokenizer for example).
There are examples in RFC 2253, section 5 [3].

I guess one of the things that is missing is a property to turn this off.
From a security perspective, my opinion tends to be to have this sort
of feature turned on by default, but I guess it may break some existing
configurations so some users may want to turn it off. (I would argue
that the motivation for using SSL/TLS is to secure the connection, so
making users aware that it's not secure because SSL/TLS wasn't fully
configured isn't necessarily a bad thing, but I also understand that
raising awareness by potentially breaking existing working
configurations can be a harsh way to do so.)

Anyway, the patch is attached, it's probably not perfect, but I'd be
happy to discuss it further if this feature is of interest to the
community. I hope this helps.

Best wishes,

Bruno.

[1] http://tools.ietf.org/html/rfc6125
[2]
http://download.oracle.com/javase/6/docs/api/javax/net/ssl/SSLSocket.html
[3] http://tools.ietf.org/html/rfc2253#section-5

Attachment Content-Type Size
sslhostname.patch text/plain 4.4 KB

In response to

Browse pgsql-jdbc by date

  From Date Subject
Next Message Bruno Harbulot 2011-08-06 20:58:03 Re: Connecting over UNIX domain sockets
Previous Message Craig Ringer 2011-08-06 12:02:08 Re: JDBC SSL hostname verification