Are you sure you know how to use crypto?
Crypto implementations of enterprise software are likely to be broken
Sometime ago I started doing security analysis; more specifically secure code reviews of enterprise software systems. I instantly realized that real world software systems more than often do not implement crypto correctly, and even more scary is that most developers don't really know how to do it right.
To be honest I wasn’t expecting that industry software was lagging behind on this matter. But, after some discussions with co-workers and reading a lot, I indeed realised that there’s a huge gap between cryptographic knowledge and software development.
Did you know that most of the examples of the implementations using crypto in popular forums/blogs are wrong?
After Googling for a while, I’ve found tones of blog/forums posts with bad examples of crypto libraries usage. Even worrying is that these blogs and forums are followed by millions of developers.
In order to draw attention to this problem, I thought it would be a good idea to expose some of the issues I’ve repeatedly been finding in enterprise applications. I believe we can do better than we are actually doing right now.
The rules everyone knows
1. Security by obscurity is no security at all
2. You should never implement your own custom crypto libraries
3. You should always use widely known and well-reputed crypto libraries
4. and finally only approved and recommend crypto algorithms should be used.
These are indeed good practices and everyone seems to follow them in a certain way or another. Nevertheless, these are not enough to correctly use crypto. To understand why, let me give some examples.
Some crypto failures I’ve been finding repeatedly
As an example, let’s consider some of the most recurrent mistakes I’ve been finding during secure code reviews when using AES.
Starting from the beginning: AES is a block cipher (symmetric crypto) where the sender and the receiver use exactly the same key to encrypt and decrypt the plaintext (i.e., a symmetric key). As any block cipher, AES needs to be used together with a mode of operation: CBC, GCM, OFB, etc. Some modes of operation are vulnerable to specific attacks and only a few of them are actually recommended.
Besides the mode of operation a proper padding scheme needs to be selected. Padding is necessary in block ciphers when the payload (plaintext) is not multiple of the block size.
Most libraries already force the usage of a mode of operation, together with a padding scheme for AES and many developers actually do this quite right, but this is not everything!
1. IVs are not random
When encrypting with a block cipher using a recommended mode of operation, you always need the so-called IV (which stands for initialization vector) and unfortunately here things go very wrong.
Security of enterprise applications is easy to break because applications don’t use secure random IVs.
The security of block ciphers highly relies on the randomness of the IV, i.e., an initial vector used in the mode of operation to prevent having the same ciphertext/output, when using the same key and the same plaintext. AES itself is a deterministic algorithm; therefore it is necessary to introduce some noise to make its output unpredictable. This typically means that for every new encryption you need to generate a new random IV.
Sadly, most implementations I’ve analyzed use a static/hardcoded IV. You’re probably asking: what should be done instead? Well, to start with, you should always use a random IV for each plaintext encryption.
Some libraries directly offer the possibility of generating a random IV when using the block cipher. Others require that you generate a random IV separately. Nevertheless, all crypto libraries provide algorithms to generate random bitstreams.
Unfortunately, not using a random IV is not the only problem I’ve encountered.
2. Hardcoded keys are everywhere
Although hardcode keys are listed as one of the worse security practices, in practice this doesn’t help anyone.
I always find some hardcode key or some key in plaintext in a configuration file.
I believe that the main reason is that everyone tells everyone that you shouldn’t be hardcoding application keys and that these should be stored securely, but no one tells you how you could do it properly.
How to store application keys securely then?
I’ve been Googling for recommendations on how to securely store application keys and things here are a bit fuzzy. Nevertheless, despite the diverse methodologies proposed, they consistently expose the reasons why you shouldn’t hardcode application keys:
1. Hardcode keys are a bad practice because if the key is compromised you have to patch the application to replace the key (this takes time and might not be always possible immediately);
2. Some hackers don’t even need the application source code to retrieve the key (binaries are just as good as source code);
3. Developers have access to the code and consequently to the hardcoded keys; if you have outsourced the development of your application, I’m pretty sure you don’t want to have a group of developers (that you don’t even know) knowing the keys used to encrypt all sensitive information of your application;
4. Keys in clear text in configuration files are as bad as hardcode keys (again, anyone with access to the application has access to the keys).
The best approach is using hardware security modules (HSM) to store your application keys, but either not everyone can afford it, or not everyone knows how to maintain/implement it. Another way is manually entering the key when the application bootstraps (e.g.: sys admin manually inputs the key). This approach has the disadvantage of requiring human intervention to bootstrap the application, which is not always feasible or desired.
A very simple (without the need of resources and rather effortless) recommended practice is storing your key encrypted in the configuration file. In this case the key is encrypted with another key (yes, another one), that is stored in a separated file (stored in an inaccessible location) and to which only the application has access rights to read from. Adding this countermeasure increases the effort required by an attacker to retrieve the applications keys, but as always, is not bullet proof.
3. Applications are not using random keys
Maybe you’re not aware of this, but you should always use random keys in your application (and everywhere else, of course) and yes, things here almost always go wrong. Let’s see why this necessary.
What does “entropy” mean to you? It may sound familiar, right? Entropy relates to randomness and measures unpredictability of information.
If a key is provided by the user (or sys admin), then usually entropy is low, i.e., it’s easy for an attacker to brute-force the key.
Keep in mind that nowadays attackers are more powerful, smart and resourceful than ever specially when having easy access to powerful cloud services. Therefore, it’s always recommended increasing the entropy of the keys you’re using.
Probably now you’re asking again: how can I do that? Well, there you are, you can, for instance, use a key derivation function (KDF). KDFs are special constructions that rely on pseudo-random functions to generate keys with some level of entropy.
Last but not least, some people defend that only the modes of operation that provide authenticated encryption should be used. I do completely agree with them, but I’m not going to address this subject now (maybe later in another post).
What should be done then?
Reading libraries documentation is always a good practice as it helps understand how you should be using them. Besides, trying to understand how the block ciphers and modes of operation work might also help understanding the tiny and nasty details of crypto implementations. A smart advice would be: do not use crypto without knowing what you’re doing. But then again, this kind of advice doesn’t help any developer to improve applications security.
So, can we conclude that it is really necessary to have a deep crypto knowledge to correctly implement crypto? From my point of view, not really, but somehow it’s necessary to create the means to help developers to do at least the basic things right. For instance,
1. By providing correct reference implementations of widely known crypto libraries;
2. Having crypto libraries with defaults set properly (unfortunately, many crypto libraries defaults are insecure);
3. Having thorough and easy to access documentation of all these libraries.
In my opinion these steps would help bridging the huge gap between these two worlds.
Bottom line is: try to keep yourself always up to date regarding security and crypto. If you can’t figure out on your own how you can produce secure software implementations, ask for help. It’s hard to get it right, but it’s possible as long as we increase awareness for these types of issues and we try to do something to improve applications security.
SIG is a very nice company based in the Netherlands committed to help technology leaders to enforce software quality, including software security, by providing a roadmap to implement security by design from the early stages of the software development lifecycle.