SSL security in HTTP::Tiny

I was asked to add SSL support to a client library, while also moving from home-grown manual HTTP code to a proper module. HTTP::Tiny was ideal because it is pure-Perl, a core module since 5.14 (so it'll be maintained), and it's just one .pm file, making it easy to ship.

An application server that supported SSL was provided for testing purposes, but the SSL certificate didn't match the hostname - HTTP::Tiny correctly rejected connections. I needed to be able to control the settings sent to the underlying IO::Socket::SSL object used for the encrypted connection so I could turn off security features for testing. As I worked on that, David Golden offered invaluable feedback, which greatly improved the design of the features added to HTTP::Tiny.

As of 0.018, HTTP::Tiny is more configurable, and has a simple interface for easily making SSL connections more secure.

By default, HTTP::Tiny can't do SSL at all, but if you install IO::Socket::SSL, it can. Unfortunately, even with that, the SSL connection will be insecure - no checking is done that the SSL certificate's CN matches the hostname of the server we're connecting to, nor is it checked for validity according to a Certificate Authority. But now, you can add

verify_ssl => 1

to the constructor to get more secure operation. The hostname will be validated, and we'll try to find a CA bundle to verify the server's certificate with. It is recommended to install Mozilla::CA, which provides a CA bundle, but we'll try to find the file your operating system provides if possible. If none are found, it is a fatal error, so installing Mozilla::CA is probably a good idea.

If you need finer control (SSL client certs, verifying the hostname but not verifying against a CA bundle, providing your own validation callback), you can pass SSL_options in the HTTP::Tiny constructor, and those SSL_* options will get passed to IO::Socket::SSL::start_SSL. It's not recommended to do that unless you know what you're doing - and that's harder than you think. The documentation is atrocious, but I think we've done a good job of hiding the best and most common security settings behind verify_ssl => 1.

TLDR: If you want SSL using HTTP::Tiny to be secure, install HTTP::Tiny version 0.018, IO::Socket::SSL and Mozilla::CA; and use verify_ssl => 1 in HTTP::Tiny's constructor.

Lessons learned

This episode has made me wonder why Perl doesn't provide SSL support out-of-the-box. You need an add-on to make it work - and not just any add-on. IO::Socket::SSL is actually not trivial to deploy. You need OpenSSL libraries installed, which requires a compiler. There are systems where that is the end of the road. Maybe you don't support them, but someone does. Unfortunately, the servers running 5.004000 probably aren't updating to 5.020000 any time soon, having SSL available in the core install would be a good first step for Perl's next decade.

Comment from Grant - April 19, 2012 at 12:56 am

It's not necessarily the case that failing to check the server certificate makes the connection "insecure". If you're using SSL because you want an encrypted connection then you get that whether you check the certificate or not. Going to the extent of checking the CA chain may even give a false sense of security given the poor record of CA's (especially if you don't also check the certificate revocation list).

However one very simple certificate check that would be quick and easy is simply to assert that the fingerprint of the server certificate *exactly* matches the one your program is configured to expect.

Comment from Robert - April 19, 2012 at 2:33 pm

"You need OpenSSL libraries installed, which requires a compiler."

That might be why it doesn't "out of the box". I don't know what considerations are made for what ships with core or not.

Comment from Olivier Mengué (dolmen) - April 20, 2012 at 8:35 am

I just wonder how long HTTP::Tiny will stay tiny ;)

I agree with you that SSL support in the perl core should be a target for 5.18. Nowadays, every machine is connected, and SSL is needed even in embeded environments.

SSL support is already bundled in StrawberryPerl, but this should be generalized.

I currently have issue ("ssl3_get_record: internal error") with AnyEvent::TLS (that uses Net::SSLeay) on Ubuntu 11.10 (latest) and I don't know how to diagnose where the problem comes from. I have some hope that a better SSL support on Perl would bring a better support of SSL on Perl on Debian/Ubuntu.

Comment from Steffen Ullrich - April 23, 2012 at 7:08 am

I'm the maintainer of IO::Socket::SSL.
While I would like to see CORE support for SSL it's definitily not easy:

- there is no SSL library which is either well supported in perl or has a usable documentation (OpenSSL definitly not) or has a good security track record.
- Root Certificates (CA) get renewed, some new gets added, others should not be trusted anymore (DigiNotar..). So you have to provide updates for older perl versions too.

If you could just use the libssl and CA infrastructure of the operating system it would be much easier.

Maybe you just have a look how other languages do in this regard:
- python uses OpenSSL too and includes a small set of CA as root
- ruby uses OpenSSL and has no builtin root-CA
- Mono(C#) uses it's own SSL implementation which cannot even check revocations (when I last looked at it, 2011)
- java: JSSE has it's own implementation. I have no idea about the used root-CA

Only java can do OCSP checking for revokations.
For all the others including perl you have to download the revokations lists and do your own checking. Mono doesn't even support this.

Comment from dagolden - April 23, 2012 at 2:27 pm

@dolmen: Tiny is relative, but HTTP::Tiny is less than 10% the SLOC of LWP. A lot of the difference, I suspect, is avoiding the overhead of using general-purpose objects for URI's, requests, responses, etc.

Currently, sloccount says HTTP::Tiny is 726 lines. R.elative to the post linked above when HTTP::Tiny was 603 lines, HTTP::Tiny added support for posting standard form data and added the SSL API improvements that Mike describes. That's a fair tradeoff for 123 lines, I think.

I don't expect much more to go in. The big missing pieces I think about for HTTP::Tiny are authentication (which is hard to do correctly without requiring or bundling non-core digesting modules) and cookie handling. LWP also provides help creating a post payload (e.g. file upload) and that seems appropriately non-Tiny.

Comment from secure socket layer info - May 13, 2012 at 3:57 pm

It’s not necessarily the case that failing to check the server certificate makes the connection “insecure”. If you’re using SSL because you want an encrypted connection then you get that whether you check the certificate or not. Going to the extent of checking the CA chain may even give a false sense of security given the poor record of CA’s (especially if you don’t also check the certificate revocation list). Thanks a lot for this information.

Pingback from Tips & tricks from my 4 months at Pythian » - May 19, 2012 at 9:15 pm

[...] SSL security in HTTP::Tiny [...]