Building secure systems is not an easy task. It is a complex problem, that requires software developers to think as hackers and look at the system as a whole, not just review their own code and call it a day.
Here are some of the lessons we have learned while building our Password Manager - Keepassa. I will try to keep this blog post platform agonistic as much as possible.
Guidelines to Build Secure Systems
- Use Encryption - Encrypt sensitive data with industry-leading algorithms. You can use AES, Serpent, or Two-Fish. It's best to combine two algorithms, to be sure that recent progress in quantitive computing will not compromise your data.
- Encrypt Your Encryption Keys - All the algorithms mentioned above use a key (a random byte sequence) to actually encrypt your data. If the keys get out you are screwed. So encrypt the encryption keys using Public/Private keys, or PGP (Asymmetric Encryption Algorithms). The main idea here is to encrypt the data with your public key and decrypt it with your private key, which you have to secure. Asymmetric encryption's performance is not great so use it only to encrypt your symmetric keys.
- Secure Your Private Keys - Don't leave your private keys unencrypted on your computer. It's best to use an HSM device, but that is not always practical. You can use an encrypted flash drive, or an encrypted file, or a specialized database like Java Keystore; but always encrypt your private keys storage.
- Use Full-Disk Encryption - Using full-disk encryption protects you from the possibility that someone will physically steal your server.
- Use a Strong Hash Function when hashing sensitive data like passwords. SHA1 is compromised. It's best to use SHA-384 or SHA-512.
- Double Hash Your Sensitive Data - For example, use SHA512(SHA384(my_password)). By using such an approach, you can protect your app from rainbow attacks, and use the second hashing function as a salt generator.
- Use Only Open-Source Libraries and Compilers to build your software. By using popular open-source software that is peer-reviewed you can be sure that security issues will be promptly addressed.
- Use a Separate Build Server that is not connected to the Internet. Don't release software, that is built on the developer machine. By using a separate build machine you can guarantee that your toolchain is not compromised. As software is built using a variety of tools, linkers, and compilers, it is extremely important that those tools are secured and trusted. Ken Thompson, has an excellent article on the subject of trusting trust.
- Secure Your Network Communications - With free services like Let's Encrypt, it's pure laziness not to use TLS for critical applications.
- Don't use SSLv3 as it is compromised. Use TLS.
- Configure Your Application to Offer Encryption Ciphers in a specific order. As not all ciphers are created equal, the SSL negotiation process tries them in a specific order. You can control this order while configuring your server. Here is a Jetty example:
sslContextFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA","TLS_RSA_WITH_AES_128_CBC_SHA","SSL_RSA_WITH_3DES_EDE_CBC_SHA","SSL_RSA_WITH_3DES_EDE_CBC_SHA"); sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA","TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"); sslContextFactory.addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3"); sslContextFactory.setIncludeProtocols("TLSv1","TLSv1.1","TLSv1.2");
Check out Part 2 of this post for additional info on how to build secure systems.