As software developers, we often have to deal with encrypting sensitive data. These days, for most of us, that simply means integrating with and enabling existing security frameworks. For web developers, it's often as easy as configuring the web server to use HTTPS. (Please note that due to the relatively narrow focus of this post I'm purposely ignoring all the other measures that need to be taken to secure websites. For a quick intro, check out the OWASP Top Ten).
One big problem that we software developers face is that encryption is notoriously difficult to get right, and a lot of us don't even realize just how many things can go wrong. Even if you encrypt a message with your secret key, then decrypt it and verify that the message is the same, you may not be securing your encrypted data sufficiently.
What can go wrong?
I don't have an exhaustive list of the possible things that can go wrong when implementing encryption; I'm not sure one even exists. I do, however, know of a few things to watch out for.
A side-channel attack is essentially an attack on a cryptosystem based on weaknesses in its implementation. Probably the most well known side-channel attack method is the timing attack. I won't go into details on how it works (that's what the Wikipedia link is for), but suffice it to say that the best way to avoid it is to never create your own encryption implementation. There are lots and lots of frameworks and other projects that provide encryption capabilities. Use them instead of implementing the encryption code yourself.
Even if you are an amazingly talented developer, you may not know everything that you need to watch out for in order to create a secure implementation of an algorithm. For example, having constant-time byte comparisons in some situations is crucial to avoiding timing attacks. There are many other nuances (most of which I don't know either) that can affect the security of your implementation. Please, just use existing code that has been vetted and, better yet, formally audited by a trustworthy organization.
It should go without saying that creating your own encryption algorithms is a terrible idea. Even encryption schemes created by respected cryptographers have been found to be cryptographically weak before, during peer review.
Did you hear about Sony's PS3 hack a few years back? They had a pretty epic misconfiguration of their firmware signature verification code. Instead of randomizing a particular variable needed in ECDSA, they accidentally kept it constant. This simple mistake allowed people to mathematically solve for the original signing key!
Just like with side-channel attacks above, the best way to avoid misconfiguration issues is to rely on an existing framework's security implementation, assuming the framework itself is trustworthy. If you can't do that, then you need to do sufficient research into the algorithm and the APIs you're using to make sure that you are providing all the necessary information in the correct format and securely randomized, as the algorithm requirements may dictate. Remember, just because you can encrypt and then decrypt some string, doesn't necessarily mean that your system is secure.
Misunderstanding of the cryptosystem
Even if you do your due diligence and "correctly" use a cryptographic algorithm or system, your implicit assumptions about it may lead you astray. For example, are you familiar with the concept of authenticated encryption? If not, then you might be in for a shock. You see, without authenticated encryption, it's possible to modify the encrypted contents of a message in such a way that, when decrypted, the message may look well-formed, but it can have incorrect information.
If you're familiar with the CIA triangle, you should know that encryption generally falls under the C, or confidentiality. But in addition to it, you also need the I, or integrity. (Naturally, all of this is moot without the A, or availability, but that's a tad out of scope.) Let's get a little more concrete.
Confidentiality without integrity: an example
To restate my earlier point, when you don't verify integrity, your otherwise valid encryption scheme can fall victim to malicious modification. Here's a somewhat realistic example. Feel free to follow along in your own console if you have OpenSSL installed. (Hint: it's available via Git Bash if you're on Windows.)
Let's say you're sending Alice an encrypted message about a money transfer to Bob. Start by creating a cleartext message.
$ echo Give Bob \$500 > clear.txt $ cat clear.txt Give Bob $500
Next, encrypt that message with OpenSSL, using 128-bit AES in CBC mode, with an IV of all zeros and password "hunter2".
$ openssl enc -aes-128-cbc -iv 00000000000000000000000000000000 -pass pass:hunter2 -in clear.txt -out cipher.txt $ xxd -c 32 -p cipher.txt 53616c7465645f5f1bfb00c57919b0082ca0dc3816b12a7ae78eddec8deb736f
If you're running this on Windows and you don't have
xxd, try this instead:
$ perl -e 'local $/; print unpack "H*", <>' cipher.txt
Now, let's decode that message with the same settings.
$ openssl enc -d -aes-128-cbc -iv 00000000000000000000000000000000 -pass pass:hunter2 -in cipher.txt Give Bob $500
In order for Alice to successfully decrypt the message like we did above, she needs to know both the IV and the password. For proper security, the IV must be random each time you encrypt something and, as it generally isn't considered to be a secret, it's often sent along with the encrypted message. The password, of course, is not sent.
But we have a problem. Mallory, a malicious person, has intercepted the encrypted message and very slightly modified it. The only change made was to the IV: Alice received the IV as 00000000000f0e0e0000030000000000. So, she decrypted the message like we've done above.
$ openssl enc -d -aes-128-cbc -iv 00000000000f0e0e0000030000000000 -pass pass:hunter2 -in cipher.txt Give Mal $600
Oh no! Suddenly, the decrypted message has been changed to make Alice think that she needs to give a different amount of money to a different person! Note that in this particular example, the IV consisting of all zeros is unimportant; this demonstration relies only on relative changes to it.
So how can we fix this problem? Is there a way for Alice to verify that the message hasn't been tampered with?
To ensure the integrity of an encrypted message, it must be authenticated. There are a number of ways to do this, some more difficult than others. But one of the simplest ways is to not use CBC mode like we did above, and instead use an authenticated mode like GCM.
Unfortunately, due to the way GCM is handled in OpenSSL, a proper demo of it using the
openssl enc command isn't possible. Regardless, GCM and other ways to authenticate ciphertext are extremely important to know about and understand.
Go forth and encrypt
In conclusion, please be aware of the various pitfalls surrounding the correct use of encryption. Understand what encryption provides and what it doesn't provide. And finally, if you're unsure about how to implement something related to security, please seek the advice of others. Just about the worst thing you can do is blindly guess - you could easily end up with a system that looks secure to the naked eye, but is completely broken when properly inspected.