If you’ve been following development around the Internet of Things you will have noticed a lot of discussion around the security requirements in the emerging industry. In fact it is a topic we have touched on a number of times in our own blogs, Feeling Vulnerable: IoT Security, Connected Cities, Part 1: Building the Connected Cities of the Future and IoT Connectivity: Getting it Right the First Time. In this blog post we want to start delving a little deeper into security and look at some of the measures you can take to secure your data.
Security can be a daunting area of programming – especially if you haven’t worked extensively in the area. With that in mind we’re going to start off with some of the more common security measures you can adopt. Specifically, we’re going to look at SSL/TLS – an area of security that we are all at least somewhat familiar with.
The most common application of SSL/TLS is as the encryption layer used in web browsing. HTTPS allows online banking and similarly sensitive applications to be carried out safe in the knowledge that your data is secure – with the possible exception of some high-profile security breaches (Heartbleed, POODLE, Apple’s goto fail;) which we will look at in more detail a little later in the post.
SSL or TLS
SSL, or Secure Sockets Layer, is the predecessor to TLS, or Transport Layer Security. SSL has three versions, which are all considered insecure due to flaws in their design. TLS was created to address the weaknesses in the SSL protocol. The terms SSL, TLS and SSL/TLS are commonly used interchangeably in literature.
It is worth noting that SSL is still in use today – in spite of it’s inherent weaknesses. The support for SSL v2.0/3.0 in a servers SSL/TLS stack is intended for backwards compatibility. Of course this support was the target of the POODLE attack. In POODLE connections are downgraded to SSL v3.0 which then allows an intruder to attack the CBC (Cipher-Block-Chaining) mode encryption method that is commonly used by SSL v3.0. The solution to POODLE attacks are for server side administrators to disable the ability to downgrade and keep their connections on the more secure TLS.
If you’re developing an application you’ll probably be in control of the server side. If this is the case you want to use TLS connections.
How Does SSL/TLS Actually Work?
Setting up an SSL/TLS session consists of three main steps – very roughly speaking, if we were to speak in detail this could be a very long post. The first step is to agree upon a cipher suite – a combination of key exchange algorithm, cipher and message authentication code. The cipher suite is agreed upon in the Client/Server Hello exchange of the SSL/TLS handshake. The second step is to exchange keys as per the agreed key exchange algorithm. And finally, the third step is to use the negotiated cipher and exchanged keys to establish an encrypted connection.
Asymmetric (public-private key) encryption is initially used to allow key exchange. Symmetric key encryption (using a generated key from the key exchange step) is then used encrypt your application data.
There are many choices for both the key exchange algorithm and the cipher used for encryption – a quick look at the IANA registry for cipher suites will convince you of this. Staying up to date with emerging security concerns is a must to inform your choice of preferred cipher suites. OpenSSL (an SSL/TLS implementation we will focus on in the next section of the post) post security advisories concerning OpenSSL on their news page. ThreatPost and CNET Security are other good sources security information.
OpenSSL is an open source implementation of SSL/TLS functionality that sees very wide spread use. If you are going to encrypt sockets yourself – as opposed to using an SSL/TLS enabled web server such as Apache – OpenSSL is a good place to start. Of course, Apache uses OpenSSL itself but all of the low-level OpenSSL calls will be built into Apache web server.
To use an SSL/TLS package you will first have to open connections that you want to encrypt. This will be done in the normal way, a connect (with possibly a manual bind) on the client side and a bind, listen, accept on the server side. Once the connection is established you can start to add the SSL/TLS encryption layer.
You’ll first need to initialise the OpenSSL library and load the error strings – very useful especially at the development stage – with the following calls.
Next you’ll need to create an SSL_METHOD, an SSL_CTX struct, an SSL struct and add BIOs to the SSL struct. The SSL_METHOD struct describes the SSL/TLS versions that are supported on your connections. The SSL struct is used to establish, read from, write to and tear down the encrypted connection. The SSL_CTX struct contains information pertinent to all your SSL connections. And the BIOs are an I/O abstraction used in the OpenSSL library; BIOs allow SSL/TLS encryption to be applied to many different data exchanges – not just the TCP/IP exchange we are concerned with. These four structs can be created and (minimally) configured using the following calls.
const SSL_METHOD* method = SSLv23_method(); SSL_CTX *ctx = SSL_CTX_new(method); SSL *ssl = SSL_new(ctx); BIO *bio = BIO_new_fd(sock,BIO_NOCLOSE); SSL_set_bio(ssl,bio,bio);
Where sock refers to the already established unencrypted socket. If you’re programming the server side you’ll also want to load private key and certificate files using the calls – of course after you have initialised your SSL_CTX *ctx structure.
Once this setup has been complete you can create the encrypted connection by calling
on the client side and
on the server side. If you’re using blocking sockets these calls should complete and you’re encrypted connection should be established easily. If you’re using non-blocking sockets you should check the return values of each call as you may need to call the functions again when the underlying socket is available for reading/writing – see the man pages of the two calls for more details.
Once the encrypted connection is established you can read from or write to the encrypted socket using the calls
number_read = SSL_read(ssl,buffer_to_read_into, number_of_bytes_to_read); number_written = SSL_write(ssl,buffer_to_write_from, number_of_bytes_to_write);
And that’s it. You now have an encrypted connection that you can read/write data over.
Your Encryption Development
A couple of final notes on encrypting your own sockets.
Firstly, if you are trying to debug your code during development it is great to be able to view the exchanges on the socket – to see what’s going wrong. Tcpdump and Wireshark are excellent tools for this task. Wireshark even let’s you load your servers private key file so that you can see the data exchanges unencrypted – assuming of course your chosen cipher suite doesn’t have forward secrecy.
Secondly (and far more importantly), the OpenSSL code snippets shown above describe a basic implementation of OpenSSL sockets. There are many more options available to you as a developer. For example, you can choose a different SSL_METHOD to use or you can restrict the list of available cipher suites to ensure certain suites are used. These options must be made as informed decisions so don’t limit your information to just this short post. OpenSSL Cookbook, a free book from Feisty Duck, is a good place to start learning more.
Finally, when you have your encryption ready to deploy you’re next step will be to get your server’s certificate signed by a trusted certificate authority (CA). By getting a trusted CA to sign your certificate, clients will be able to authenticate your server as part of the connection setup. It is very important to add this step into your client side code. Failing to do so opens up your platform to a man-in-the-middle attack. Apple’s goto fail; bug was a high-profile example of failing to authenticate with a certificates CA.
Happy coding and stay tuned for part 2 of this security series next week!