Chapter 6 ■ tLS/SSL
105
Note that although the ssl module’s default contexts do not require a PFS-capable cipher, you will probably get
one anyway if both your client and server are running against recent-enough versions of OpenSSL. For example, if I
start up the safe_tls.py script given in Listing 6-3 in its server mode and connect to it with the test_tls.py script
you will meet in Listing 6-4, then (given my particular laptop, operating system, and OpenSSL version) I can see that
the Python scripts have given priority to the PFS-capable elliptic curve Diffie–Hellman exchange (ECDHE) cipher
without my even my asking.
$ python3.4 test_tls.py -a ca.crt localhost 1060
...
Cipher chosen for this connection... ECDHE-RSA-AES256-GCM-SHA384
Cipher defined in TLS version....... TLSv1/SSLv3
Cipher key has this many bits....... 256
Compression algorithm in use........ none
Thus, Python will often make good choices without your having to be specific. Nonetheless, if you want a
guarantee that a particular protocol version or algorithm is put into use, simply lock down the context to your specific
choices. As this book is going to press, for example, a good server configuration (for a server that will not be expecting
clients to offer TLS certificates and thus can choose CERT_NONE as its verification mode) is as follows:
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.verify_mode = ssl.CERT_NONE
context.options |= ssl.OP_CIPHER_SERVER_PREFERENCE # choose our favorite cipher
context.options |= ssl.OP_NO_COMPRESSION # avoid CRIME exploit
context.options |= ssl.OP_SINGLE_DH_USE # for PFS
context.options |= ssl.OP_SINGLE_ECDH_USE # for PFS
context.set_ciphers('ECDH+AES128 ') # choose over AES256, says Schneier
You can substitute these lines of code into a program like Listing 6-3 whenever a server socket is being created.
Here the exact TLS version and cipher have been locked down to only a few explicit options. Any client trying to
connect that cannot support these choices will fail instead of establishing the connection successfully. If you add the
previous code to Listing 6-3 in place of the default context, then a client attempting a connection with an even slightly
older version of TLS (like 1.1) or a slightly weaker cipher (like 3DES) will be rejected.
$ python3.4 test_tls.py -p TLSv1_1 -a ca.crt localhost 1060
Address we want to talk to.......... ('localhost', 1060)
Traceback (most recent call last):
...
ssl.SSLError: [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:598)
$ python3.4 test_tls.py -C 'ECDH+3DES' -a ca.crt localhost 1060
Address we want to talk to.......... ('localhost', 1060)
Traceback (most recent call last):
...
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:598)
The server in each of these cases will also raise a Python exception diagnosing the failure from its own point of
view. And thus the resulting connection, if it succeeds, is guaranteed to be using the latest and most capable version of
TLS (1.2) with one of the best ciphers available to protect your data.