Securing sockets with TLS/SSL

You have probably come across the discussion around secure web communication using Secure Socket Layer (SSL), or more precisely Transport Layer Security (TLS), which is adopted by many other high-level protocols. Let us see how we can wrap a plain sockets connection with SSL. Python has the built-in ssl module, which serves this purpose.

In this example, we would like to create a plain TCP socket and connect to an HTTPS enabled web server. Then, we can wrap that connection using SSL and check the various properties of the connection. For example, to check the identity of the remote web server, we can see if the hostname is same in the SSL certificate as we expect it to be. The following is an example of a secure socket-based client:

import socket
import ssl
from ssl import wrap_socket, CERT_NONE, PROTOCOL_TLSv1, SSLError
from ssl import SSLContext
from ssl import HAS_SNI

from pprint import pprint

TARGET_HOST = 'www.google.com'
SSL_PORT = 443
# Use the path of CA certificate file in your system
CA_CERT_PATH = '/usr/local/lib/python3.3/dist- packages/requests/cacert.pem'

def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, ca_certs=None, server_hostname=None, ssl_version=None):

    context = SSLContext(ssl_version)
    context.verify_mode = cert_reqs

    if ca_certs:
        try:
            context.load_verify_locations(ca_certs)
        except Exception as e:
            raise SSLError(e)

    if certfile:
        context.load_cert_chain(certfile, keyfile)

    if HAS_SNI:  # OpenSSL enabled SNI
        return context.wrap_socket(sock, server_hostname=server_hostname)

    return context.wrap_socket(sock)

if __name__ == '__main__':
    hostname = input("Enter target host:") or TARGET_HOST
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_sock.connect((hostname, 443))
    
    ssl_socket = ssl_wrap_socket(client_sock, ssl_version=PROTOCOL_TLSv1, cert_reqs=ssl.CERT_REQUIRED, ca_certs=CA_CERT_PATH, server_hostname=hostname)

    print("Extracting remote host certificate details:")
    cert = ssl_socket.getpeercert()
    pprint(cert)
    if not cert or ('commonName', TARGET_HOST) not in cert['subject'][4]:
        raise Exception("Invalid SSL cert for host %s. Check if this is a man-in-the-middle attack!" )
    ssl_socket.write('GET / 
'.encode('utf-8'))
    #pprint(ssl_socket .recv(1024).split(b"
"))
    ssl_socket.close()
    client_sock.close()

If you run the preceding example, you will see the details of the SSL certificate of a remote web server such as http://www.google.com. Here we have created a TCP socket and connected it to HTTPS port 443. Then that socket connection is wrapped into SSL packets using our ssl_wrap_socket() function. This function takes the following parameters as arguments:

  • sock: TCP socket
  • keyfile: SSL private key file path
  • certfile: SSL public certificate path
  • cert_reqs: Confirmation if certificate is required from other side to make connection and if validation test is required
  • ca_certs: Public certificate authority certificate path
  • server_hostname: The target remote server's hostname
  • ssl_version: The intended SSL version to be used by the client

At the beginning of the SSL socket wrapping process, we have created an SSL context using the SSLContext() class. This is necessary to set up the SSL connection specific properties. Instead of using a custom context, we could also use a default context, supplied by default with the ssl module, using the create_default_context() function. You can specify whether you'd like to create client or server side sockets using a constant. The following is an example for creating a client side socket:

context = ssl.create_default_context(Purpose.SERVER_AUTH)

The SSLContext object takes the SSL version argument, that in our example is set to PROTOCOL_TLSv1, or you should use the latest version. Note that SSLv2 and SSLv3 are broken and must not be used in any production code for serious security issues.

In the preceding example, CERT_REQUIRED indicates that server certificate is necessary for the connection to continue, and this certificate will be validated later.

If the CA certificate parameter has been presented with a certificate path, the load_verify_locations() method is used to load the CA certificate files. This will be used to verify the peer server certificates. If you'd like to use the default certificate path on your system, you'd probably call another context method; load_default_certs(purpose=Purpose.SERVER_AUTH).

When we operate on server side, usually the load_cert_chain() method is used to load the key and certificate file so that clients can verify the server's authenticity.

Finally, the wrap_socket() method is called to return an SSL wrapped socket. Note that, if OpenSSL library comes with Server Name Indication (SNI) support enabled, you can pass the remote server's host name while wrapping the socket. This is useful when the remote server uses different SSL certificates for different secure services using a single IP address, for example, name-based virtual hosting.

If you run the preceding SSL client code, you will see the various properties of the SSL certificate of the remote server, as shown in the following screenshot. This is used to verify the authenticity of the remote server by calling the getpeercert() method and comparing it with the returned hostname.

Securing sockets with TLS/SSL

Interestingly, if any other fake web server wants to pretend to be the Google's web server, it simply can't do that, provided that you check the SSL certificate that is signed by an accredited certificate authority, unless an accredited CA has been compromised/subverted. This form of attack to your web browser is commonly referred to as the man in the middle (MITM) attack.

Inspecting standard SSL client/server communication

The following screenshot shows the interaction between the SSL client and the remote server:

Inspecting standard SSL client/server communication

Let us examine the SSL handshake process between the client and the server. In the first step of a SSL handshake, the client sends a Hello message to the remote server saying what it is capable of, in terms handling key files, encrypting messages, doing message integrity checks, and so on. In the following screenshot, you can see that the client is presenting a set of 38 cipher suites to the server to choose relevant algorithms. It also sends the TLS version number 1.0 and a random number to generate a master secret for encrypting the subsequent message exchanges. This is helpful for preventing any third party to look inside the packets. The random numbers seen in the hello messages are used to generate the pre-master secret, which both ends will process further to arrive at the master secret, and then use that to generate the symmetric key.

Inspecting standard SSL client/server communication

In the second packet from server to client, the server selects the cipher suite TLS_ECDHE_RSA_WITH_RC4_128_SHA for the purpose of connecting to the client. This roughly means the server wants to use the RSA algorithm for key handling, RC4 for encryption, and SHA for integrity checking (hashing). This is shown in the following screenshot:

Inspecting standard SSL client/server communication

In the second phase of the SSL handshake, the server sends an SSL certificate to the client. This certificate is issued by a CA, as mentioned earlier. It contains a serial number, public key, validity period, and the details of the subject and the issuer. The following screenshot show the remote server certificate. Can you locate the server's public key inside the packet?

Inspecting standard SSL client/server communication

In the third phase of the handshake, the client exchanges a key and calculates a master secret to encrypt the messages and continue further communications. Client also sends the request to change the cipher specification that was agreed on the previous phase. It then indicates to start encrypting the message. The following screenshot shows this process:

Inspecting standard SSL client/server communication

In the final task of the SSL handshake process, a new session ticket is generated by the server for the client's particular session. This happens due to a TLS extension where the client advertises its support by sending an empty session ticket extension in the client Hello message. The server answers with an empty session ticket extension in its server Hello message. This session ticket mechanism enables the client to remember the whole session state, and the server becomes less engaged in maintaining a server-side session cache. The following screenshot shows an example for presenting an SSL session ticket:

Inspecting standard SSL client/server communication
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.234.188