POODLE is a vulnerability that effectively lets a third party become a “man in the middle” on SSLv3-encrypted network connections. This is very bad from a security point of view. If you have not done so already, go to Wikipedia’s POODLE page and read more information about it. This post shows how to deal with the POODLE vulnerability for Python Tornado by turning off SSLv3.

Testing for POODLE

One of many ways to test for POODLE is to run the s_client that comes with your OpenSSL installation. Invoke it as follows:

openssl s_client -connect ${HOST}:443 -ssl3

(If you run SSL on a non-standard port, change the port number from 443 to whatever you are using.)

There will be a lot of output, regardless of whether the host is vulnerable to POODLE or not. But the important bit is whether your s_client manages to negotiate an encryption cipher or not, as shown in the output here from a vulnerable site:

> openssl s_client -connect mail.google.com:443 -ssl3
[a lot of output omitted]
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : ECDHE-RSA-RC4-SHA
[more omitted output]

Whereas a site that has SSLv3 disabled will look like:

> openssl s_client -connect duckduckgo.com:443 -ssl3
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : 0000

So how do we disable SSLv3 in our Python Tornado projects?

POODLE be gone!

Python Tornado uses the ssl library that comes with Python. In its wrap_socket method, one can pass the allowed SSL/TLS version as a parameter called ssl_version. The documentation shows the allowed values that the parameter can be set to. Choosing the oldest possible secure version ensures the highest possible compatibility, since not all software supports the latest versions of these protocols. For that reason, one should use ssl.PROTOCOL_TLSv1 as the protocol.

This change is applicable for all projects that use Python’s own ssl library, and should be immediately applied.

For Tornado, this information should be passed to the tornado.httpserver.HTTPServer constructor, similar to:

self.server = tornado.httpserver.HTTPServer(
    application, io_loop=self.ioloop,
    ssl_options={
        "certfile": self.server_cert,
        "keyfile": self.server_key,
        "cert_reqs": self.cert_reqs,
        "ca_certs": self.ca_certs,
        "ssl_version": ssl.PROTOCOL_TLSv1
    })

That’s all you have to do!

Note about modern Pythons (version 2.7.9 and Python 3)

This blog post has been focused on Python 2, and in particular versions prior to 2.7.9 (as 2.7.6 is the one in Ubuntu 14.04, this is what may will encounter in the wild). More modern versions of Python have the SSLContext that wraps not only the protocol to use, but also any other settings that one would like to use and re-use in an application. Use of it is simple and requires only pass the protocol constant as the parameter to the constructor. Tornado can, when used with Python 3.2 and above, accept an SSLContext object instead of the dict in our example above, although the dict should for the foreseeable future still be supported.

Leave a Reply