Securing Your App: The Web Side
For anyone with a passing interest in developing apps or who has made an app that makes use of a remote web service, listen up. Much as it can be dull to talk security, particularly when it comes to Android applications, it’s still necessary. Today though, I’m going to go through some suggestions for securing applications that make use of remote web services. Whether this is a server to store data on or a server to deal with communications and messages being sent between users, it’s always worth paying attention to a few things that are often overlooked.
Refer to the headline image. If you want to take only one thing from this article, it’s simple: Encrypt all the things! Encrypt everything you can. Sure, there is a trade-off, but if there’s ever any communication between your application and a web site, encrypt it!
Just encrypting it isn’t enough; you also have to do it properly. The most popular form of transport encryption in use today is SSL. This is what’s used when you browse to a site prefixed by https://. Simply using an HTTPS site though isn’t enough! It’s of critical importance that you ensure the SSL implementation is done correctly. A number of Android apps fail in this regard.
On top of that, if your app is likely going to be used by users in a country where ISP or government level interference may result in spoofed SSL certificates being issued by valid CAs (it happens more often than you’d think), you need to be using certificate pinning, which is now included in Android. But to ensure all users benefit, you should really implement it yourself within your app. Really, everyone should be using certificate pinning. Otherwise, CCNIC (China Internet Network Information Center (CNNIC), the state network information center of China), among others, is able to issue SSL certificates for your domain and intercept traffic going through its networks, and there’s nothing you can do to stop it. Pin the certificates by public key, and ensure only those certificates are accepted.
Also consider using SSL with forward secrecy to ensure an attacker sniffing traffic to and from the server cannot decrypt it in future with your SSL keys, but ensure you are aware of the risks of SSL/TLS downgrade behaviour as well.
Authentication is often overlooked when designing a system. Authentication is the process of the client and remote server establishing they are communicating with each other, and not an untrusted middleman, relaying the conversation while logging the decrypted contents.
SSL, when used correctly with certificate pinning, offers a reasonably reliable assertion to the user that the server they are communicating with is the correct one, and not one set up to “pretend” to be the valid server by a malicious party. When designing your app, always ensure that clients verify the server is genuinely the server it should be. Likewise, ensure the server authenticates the client properly. And while I won’t use this occasion to lecture about strong versus weak authentication, I strongly suggest that if you authenticate users by sending a username and password (even over SSL), that you consider stronger authentication, such as challenge-response. If done correctly, you need never even know the user’s password!
Also ensure that your authentication process is not defeated by your password reset process! You can have the most secure, multi-factor security system possible, with paper-based verification keys and SMS verification, but your application is not secure if a user can reset their password in a couple of button presses, and lead to security dropping to that of the lowest common denominator (their email account security). For most users, their email should not be regarded as secure. It may be difficult to avoid this, but consider using email as part of the verification process, rather than the only proof needed before resetting a password.
Consider if the user should even be able to reset their password; sometimes it simply isn’t possible if the user’s password is used to encrypt an AES key, which is used to protect their account. In this case, the only option available is to delete the account and for them to lose their data. This sounds inconvenient, but it’s a lot better than there being a password reset system in place, which you could be forced through legal pressure to use on one of your users. If your service wants to be high security, there should not be any password reset possible, because you should be using per-user encryption on their data, with no backup of the key that can be used without their password.
Why? No, not to irritate your users. Rather, because you, as a service operator who cares about security, has implemented proper encryption of all user data and metadata, using a key unique to that user, and derived from his/her password. In an ideal situation, this password is never even communicated to the server (due to strong authentication with zero-knowledge proof being used). While this might sound like overkill, it’s far from it, and it’s already in use by some privacy-cautious services.
3. Secure Your Server
This is slightly less related to Android, but it’s just as important (or likely more important, being realistic) as using encryption and strong authentication of both user and server. If you send data to a server, every one of your users has the details of this server. Don’t try and pull your “Cloudflare” or “DDOS protected” card here. Those don’t mask the server sufficiently to prevent a determined attacker from finding the server. Here’s a few tips:
a) Disable Password-based SSH logins. If you allow password based SSH logins, you should give up and go home, as you are a liability to the world. Start using public/private keypair authentication, and use good, strong keys, which you generated on your local PC, and store securely. Password protect your private key, and you have achieved “almost” two-factor authentication, through something you have (your SSH key, securely stored on a USB flash drive, removed when not in use), and something you know (your passphrase for the SSH key, which is random and unguessable). For added security, consider restricting the IP addresses able to log into the server remotely, at firewall or network ACL level.
If you use the server for other tasks (particularly common for smaller “projects”, you need to consider the risks to user data if another service on the same server is compromised. Is there really a need to host a blog on the same server? That blog is presumably running under the same HTTP daemon, and just waiting to be compromised.
If you want to be extra safe, consider storing your web application code on read-only media. Store it on a CD-ROM, and cache it into RAM if you wish. This prevents an attacker from compromising the software code if they gain access.
d) Be Transparent
There’s no security through obscurity. Don’t hide the security measures you put in place – a determined attacker will find them out. Show the world (and your users) just how much you care about their security, and be open with them. Tell them what you do to protect their data from unauthorised access of any kind. Don’t make security an afterthought – show people you thought about security while designing your app and its web service.