Why Your Website Is Insecure – Cryptosystem Basics
We have witnessed lots of new websites and mobile apps sprouting out of this tech bubble; many of which are built by inexperienced developers or developed in a hurry by the impatient entrepreneur. Consequently, we hear too frequently that some website was hacked or that a server holding sensitive data was compromised. Most of us brush it off with a “Whew! It didn’t happen to me.” Well, how many websites or services have you joined? How many of these sites/services share the same password? I’m pretty sure you don’t have a unique password for each site or service you signed up for. How many of these dot coms store your personal information? It should be a concern. This is why I’m hesitant to register for that trendy new silicon valley startup dot com; I cringe at the lack of security practices employed by many developers. The negligence is almost criminal. Displaying a GoDaddy secure logo or McAfee secure seal doesn’t mean crap. This false sense of security stems from the fact that the site complies with some arbitrary checklist of common exploits (eg. XSS, SQL injection).
I don’t claim to be an expert in security, but allow me to share some cryptosystem basics with you.
This is where you use a cryptographic hash function to encrypt (or hash, rather) your passwords. Hash functions go one way… meaning once you encrypt your password, your password cannot be “decrypted” back into plain text.
Enc(Plaintext)->Cipher exists, nevertheless
Dec(Cipher)->Plaintext does not. When a user logs in, hash the entered password and compare the new hash with the old hash that you have stored.
However, there is a problem. Running
php -r "echo md5('password');" returns 5f4dcc3b5aa765d61d8327deb882cf99. I can run it 100 times and it will always return that value. I now know that a hash of 5f4dcc3b5aa765d61d8327deb882cf99 means the plain text version of the password is “password”. With a few lines of code, I can create a script that brute forces a md5 hash of every alphanumeric combination and store each of those hashes in a table. This is also known as a “rainbow table”. A rainbow table makes it very easy to reverse lookup a hash and return the unhashed text. So by storing “5f4dcc3b5aa765d61d8327deb882cf99” in your rainbow table, next time you run across that hexadecimal, you now know it equates to the plain text “password”. To protect against rainbow table attacks, use a salt. What is a salt?
md5("thisisasalt"."password") That is a salt. It’s an arbitrarily long string that is prepended to the password before it is hashed.
MD5 isn’t known to be a secure cryptographic hash function and is not recommended. I have heard of hackers utilizing cloud computing to unhash MD5 passwords in a matter of seconds. Instead, use Bcrypt. Not only does Bcrypt implement a salt, you can increase the iterations to make it slower by (2^n). In other words, it adapts to the times and makes it very difficult to brute force despite the increase in processing power. But no matter what, always enforce long alphanumeric passwords that aren’t in the dictionary. This will make your password very difficult to brute force.
Sensitive Data (transport)
Do you recall middle school? Imagine you are in a classroom and you want to pass a sensitive letter to your friend sitting across the room. What can you do to ensure that only your friend can read the message? This is similar to entering credit card information to make an online purchase. I’ve demonstrated how easy man-in-the-middle attacks are in previous blog posts and we want to prevent anybody but the recipient from reading our message. If you encrypt the message with a symmetric cryptographic function, sure your recipient will be able to decrypt the message but at some point, it would have been necessary for you to agree upon a key.
Passing a note with an encrypted message along with the key is not safe for obvious reasons. This is where “asymmetric cryptographic functions” are useful…. or Public-key encryption. When you log onto a banking website or an ecommerce site, your browser SHOULD always display a lock icon to let you know that public-key encryption has been enabled. How does public key encryption work? Each party has 2 keys: A private key and a public key. The public key can only encrypt and the private key can only decrypt. You allow everybody access to your public key but NOBODY should be able to access the private key except yourself. Let’s say the names of the two friends are Alice and Bob. Each has a public key and a private key. It would go something like this. Alice passes her public key to Bob. Bob encrypts his message with Alice’s public key.
Encrypt("message", Alice's public key) -> cipher. Bob has now generated a cipher which only Alice can decipher. Bob passes the cipher to Alice. Alice decrypts the message with her own private key.
Decrypt(cipher, Alice's private key) -> message! This is how public key encryption works. Here is an interesting fact: The security surrounding today’s most commonly used public key encryption is based on the difficulty of factoring the product of two very large prime numbers. What??? Yes. Think about how hard it is to factor the product of two large prime numbers. There is no easy systematic approach. Now you know why engineers and mathematicians go nuts over the discovery of insanely large prime numbers!
Sensitive Data (storage)
I remember reading an announcement from a hacker group called “antisec” bragging about breaking into the specialforces.com website and stealing passwords and credit card information. They mentioned that the information was encrypted using Blowfish encryption (which is a very strong symmetric cryptosystem).
BFencrypt(message,key) -> cipher
BFdecrypt(cipher,key) -> message
Now, I can guarantee that they didn’t “crack” the cryptosystem or find a flaw in the encryption algorithm. No, they found the key which was apparently lurking in the system as well. I don’t think I need to explain the stupidity of that. I mean, you can buy a brand spanking new, state of the art LOCK for your door, but if you leave the key in the lock, it’s pretty useless. It’s like having an unbreakable combination lock that has a sticker on the clasp with the combination written on it.
Your lock is only as good as the key (or where you store the key). If you’re storing your customers’ sensitive information, 1) pick a strong symmetric cryptosystem. 2) select a key that is unique to each user, and 3) do NOT store the key in your database or within your codebase.
Personally, I accomplish this by encrypting their data with their plaintext password or hashed password as the key (using any type of hash besides bcrypt hash). Since the password is not stored in my DB in plaintext (or decipherable ciphers) and the key is unique per customer, it would be virtually impossible to retrieve the password therefore virtually impossible to decipher the sensitive data. When the customer is on the site and must access this information, all I need to do is re-prompt the customer for his/her password and use that string to decrypt the respective data. Following me?
Developers and CEOs, please take these precautions. Security should always be first. Your users trust you to hold their data, therefore YOU are responsible. Obfuscation is NOT security.
If and when some genius proves (or disproves) the Riemann hypothesis and then discovers a non-brute-force method of finding prime numbers, the entire world’s security will be at risk and I shall update this post. Until then, stay safe.