Tuesday, February 4, 2014

How MITM attack works and preventing NSA from snooping on your traffic

Nowadays, security has become a really big concern. If you are storing any user data on your servers, you should seriously consider encrypting all communications between your client and server using SSL.

Again, simply using SSL is not enough. There are lots of things people need to understand about SSL before they can assume that their SSL encrypted traffic is safe. One major thing that people need to watch out for is the Man-in-the-middle (MITM) attack. Let me explain how an MITM can be done.

Server ======> Client
               SSL

When a client interacts with the server through SSL encryption, all data is exchanged in a form which is not readable by anyone. Say, if a cracker was to listen to an already established SSL session, he will see encrypted data which he can't decrypt. In this case, the communication is fully secure.

But what if the cracker somehow inserts himself in the middle of the client and server even before the SSL connection was established?

Server ======> Cracker ======> Client
              SSL                        SSL

This scenario will mean that the cracker acts as server to the 'Client' and a client to the 'Server'. Thus the cracker can read all data being exchanged without having to bother about breaking the SSL connection. Please note that there still are 2 SSL connections being established, and both these connections are fully secure. But this kind of vulnerability can be exploited if the cracker can hijack the connection before it is established.

In order to prevent this kind of MITM attacks, the SSL certificates are required to be signed by a Certification Authority (CA). Say, a user tries to access data on the encrypted channel. The Server sends a certificate signed by a trusted CA, to the Client, and the Client already having a list of trusted CAs can verify that this certificate is indeed genuine. If a cracker tries to insert himself in the middle, the cracker will have to submit a certificate to the Client, but he can't get it signed by a trusted CA, so the Client will be shown an exception.

Remember, when your browser suddenly shows a warning about an untrusted HTTPS connection, and asks whether you want to add an exception? In devices like Android, the SSL connection will not be established at all, since the certificate was not signed by a trusted CA.

There are various things that we can deduce out of this:
1. This prevents MITM attacks effectively, except in rare cases which I will get to in a short time.
2. If you have a self-signed certificate on your server, then there is no way the Client can know whether he is being a victim of MITM attack or not. (since the cracker in the middle will also have a self-signed certificate). Thus self-signed certificates in production mode are a strict no-no, unless you can send your self-signed certificate to the user in a separate secure channel.
3. If you have a self-signed certificate on the server, and you just want to use in testing mode in your app, your client app will not allow SSL connection to happen.

Particularly, we were faced with the issue that we were trying to run an android app called ODK-collect with a self-signed certificate on the server (for testing). Since android didn't see the certificate as trusted by a CA, it didn't allow the connection to take place. As explained before, this is by design to prevent MITM attacks.

If you are asking why I would like to have self-signed certificate on the server when I previously recommended against it, there can be various reasons for doing that:
1. Maybe you are stuck with talking to a server which uses a self-signed certificate. You have no control over the server but are working on an app that has to talk to this server.
2. You want to test your client-server connection using self-signed certificate. I am not sure if you can obtain a CA signed certificate for hostname as localhost.
3. Even free certificate provider like CAcert may sign your certificate, but CAcert is not treated as trusted CA by most browsers and clients, so they are treated at par with a self-signed certificate.
4. Maybe you just want to avoid the hassle of paying for a CA to sign your certificate, and want to go for self-signed approach.

So, we were trying to find out how to use self-signed certificate and getting ODK-collect (any android app for that matter) to communicate with our server. There are various options available:

1. Trust all certificates
Stack Overflow and other blogs are filled with answers that will allow your app to trust all certificates by default. Even though most answers say that this should only be done in testing mode, what if somebody forgot to turn it off in production mode? In case you are wondering, if your android app trusts all certificates, then your client will never be able to detect MITM attack and will continue to do business as usual. So, in case you want to do it like this, you should exercise extreme caution that this code doesn't go in production. In fact, if running your app with your own self-signed certificates is so easy, why even do this?

2. Install your certificate using SD card method
You can install the certificate using SD card method. If you are testing on a few devices, yes, you could do this. But if you are using this on many devices, there is the problem of having to do this manual method. In case you are going to use self signed certificate in production, you will have to ask all your users to manually install the certificate. Hardly a feasible scenario.

3. Packaging the certificate within your android app and allowing an SSL connection only with the same certificate, disallowing all others
This is the preferred route. What you do is - put the certificate within your apk, change your HTTPS connection to only allow this certificate and discard the rest.

Let me come back to an important issue that I didn't address previously. Say, you have a certificate signed by a trusted CA on the server, you may think that you are now fully secure. But unfortunately, there is a scenario when the cracker could get hold of a fake certificate signed by a trusted CA, and do an MITM. The end-points of the connection channel have no way to know that their traffic is being snooped by the cracker. You think this is highly unlikely? What if the cracker turns out to be an influential organization like the NSA?
Read this: https://www.schneier.com/blog/archives/2013/09/new_nsa_leak_sh.html

You may say that NSA is a spying agency that is trying to protect American interests. But what if there was some spurious government organization that uses its own influence to make some trusted CA sign their fake certificates? Definitely possible. Worst part is, there is not much you can do to prevent such kind of an attack. This is precisely why people like Schneier are saying that the Internet is broken.

But if your android app only accepts your self-signed certificate, and not even certificates from a trusted CA, then this kind of influential attack can be prevented, since you are only allowing your own certificate for communication, nothing else. Note that the certificate has to reach the user in a safe and secure manner, in this case, packaged within the android package (.apk). Another note: you have to ensure that when the user downloads your apk, it is not changed in transit by a cracker (use checksums etc. to verify the downloaded apk). We are currently using this methodology for our new product called Form Factor.

Enough been said. In my next blog post, I will talk about what code you need to write to get this done. This will not only apply to ODK-collect, but any android application where you want to get running with self-signed certificate on your server.