17

We have a mobile app for iOS and Android available in the Apple and Google Play stores. The app communicates with our server’s Web Services over HTTPS.

We have attackers able to spoof the app traffic. This probably means our attackers are decrypting and monitoring the HTTPS-encrypted Web Services request/response with a tool such as Fiddler.

Our next step is to encrypt inside the mobile app. We encrypt the Web Service payload in both directions using AES in CBC mode with HMAC. This means both our server and the downloadable mobile app have a shared secret key (actually, at least two).

The problem is that it’s quite possible to reverse-engineer iOS or Android mobile apps and harvest any secret keys. To be sure, we have a higher level of security. It takes more expertise to reverse-engineer a mobile app than it takes to observe HTTPS traffic via Fiddler.

We may not be a big enough target for anyone to bother with harvesting our secret keys. But we’d like to grow to the point where the attack is worthwhile!

Any key-exchange protocol that I’m aware of requires the mobile app to have a secret key somewhere in the process. With the app available as a free download from Apple App Store and Google Play, I simply have no way to ensure its secret remains secret.

Arxan.com claims to provide an Enterprise-grade solution which appears to be a sufficiently-complex process of hiding the keys from would-be attackers. I’m not yet ready to ask them about pricing!

Looked at one way, my question is, “How do I protect a secret key in a mobile app in the wild?” I have seen two clear answers:

  • You can’t.
  • Use HTTPS. However, we do this and it’s clear to us that our traffic is still being monitored.

So, I think my question becomes one of Authentication as opposed to Authorization: On the server side, how do I distinguish between an attacker pretending to be our mobile app, and legitimate traffic from our app?

Assuming that we are encrypting and correctly using HMAC, a valid message tells us:

  • The message has not been altered.
  • The sender is in possession of our shared secret key(s).

Everything to this point depends on the secret key not being compromised.

The only answer I’ve thought of so far is traffic analysis. We detect brute-force attack traffic and certain other traffic profiles. We detect various forms of replay attack.

We could thus infer that our secret key HAS been compromised, if it ever is, and invalidate the key (thus invalidating all mobile installs in the wild). But it stands to reason that if an attacker can figure out one secret key, that attacker can figure out the next key as well. Nor do we want to invalidate our entire legitimate installed-app user base!

We can install a certificate inside our app, but does that solve the problem? And if so, how? Don’t we have the same issue of the app being reverse-engineered and spoofed?

EDIT: What are we trying to protect?

  1. Our attacker can run a password list against us by spoofing our app's User Login web sequence. Since our attacker is able to observe HTTPS traffic as it exits the app, our attacker knows how to construct the web service request and authentication.

  2. We can encrypt the login (and other) information before it leaves the app. This should prevent both sniffing and spoofing, assuming our attacker does not possess the secret keys used in the encryption.

  3. Given that inbound (to the server) traffic is more important to protect, Public Key encryption would be a possibility. Only the server could decrypt it. However, since the key is public, our attacker could still run a password list pretending to be the app.

So what are we trying to protect? We're trying to protect ourselves from an attacker breaking into user accounts via our web services. Specifically, how might we protect ourselves from an attacker sufficiently motivated to reverse-engineer our mobile app in the wild?

SECOND EDIT: Let's try restating the problem.

How might we design a robust and secure Web Services API? It must be able to withstand observation by a would-be attacker. We must be able to distinguish and reject spurious messages from a would-be attacker.

Specifically, we want to:

  • Prevent our attacker from using our Web Services API to run a password list and potentially log in to a member account. We have other brute-force detection measures in place, but we'd like to directly secure our API from such attack.

  • Prevent our attacker from using our Web Services API to probe our server for other attack possibilities.

Edward Barnard
  • 672
  • 6
  • 17
  • 8
    What part about "you can't control the client" are you not getting? – Neil McGuigan Sep 12 '15 at 19:10
  • @NeilMcGuigan Exactly. To rephrase the question: Given that I can't control the client, are there things that I can do to securely communicate with the client and refuse communication with the attacker? – Edward Barnard Sep 12 '15 at 19:18
  • 5
    It is not clear what you are trying to protect. If you want to protect the communication between client and server against an attacker then HTTPS is good. If you fear that the attacker and the client are the same person which want to reverse engineer the application (like to gain advantage in a game) than HTTPS will not help and encryption on the client devive will not help too. Your only choice is then to make the reverse engineering hard by obfuscation, anti-debugging and similar techniques or simply don't trust the client (i.e. keep the state of the game at the server not the client). – Steffen Ullrich Sep 12 '15 at 19:45
  • @SteffenUllrich I edited the question to be more clear. We do keep all relevant state on the server side. We allow our users to freely switch between mobile devices, or to the desktop (web browser) interface. I have to assume mobile banking apps have this figured out. But they are not so likely to share that knowledge here! – Edward Barnard Sep 12 '15 at 20:22

4 Answers4

26

I think I can help resolve your concerns.

So what are we trying to protect? We're trying to protect ourselves from an attacker breaking into user accounts via our web services. Specifically, how might we protect ourselves from an attacker sufficiently motivated to reverse-engineer our mobile app in the wild?

You can't. It's that simple. Trying to do the impossible is what's causing your concern. Instead, design and implement your server under the assumption that the client may be your app or it may be some unknown malicious client. If you can't secure your app with these assumptions, then you will need to buy into shipping an insecure app (likely bad a bad choice) or only have clients in locations where you have physical control (also likely a bad choice).

Let me address some of your specific statements:

I have to assume mobile banking apps have this figured out. But they are not so likely to share that knowledge here!

No. This is wrong. I'm not sure who you think these secretive mobile banking app developers are but I can assure you that many on this site have developed banking apps, mobile or otherwise, and helped secure them.

Our attacker can run a password list against us by spoofing our app's User Login web sequence. Since our attacker is able to observe HTTPS traffic as it exits the app, our attacker knows how to construct the web service request and authentication.

Absolutely right. The attacker can execute a brute-force password attack on your server. Techniques such as account lockout, exponential delay, and CAPTCHA will help defend against this.

We can encrypt the login (and other) information before it leaves the app. This should prevent both sniffing and spoofing, assuming our attacker does not possess the secret keys used in the encryption.

Given that inbound (to the server) traffic is more important to protect, Public Key encryption would be a possibility. Only the server could decrypt it.

Trust SSL. It is well tested and works well. There's no need to have app-based encryption to protect against a MiTM attack. I know it is freaky to hook up a debugging proxy like Fiddler and see your supposedly encrypted traffic in clear text, but it's not really a security problem. It's the user's data, let them see it if they want. And being the server was written with the concept that it can never know what app the client is running, your protocol should be able to withstand examination by a would-be attacker. Note that you must still do proper SSL validation to prevent a 3rd-party MiTM attack.

So skip the HMAC and client-side app encryption when using SSL. Instead, write a robust and secure API. Not because I say so, but because there is no alternative. Your own analysis proved this.

EDIT:

Prevent our attacker from using our Web Services API to run a password list and potentially log in to a member account. We have other brute-force detection measures in place, but we'd like to directly secure our API from such attack.

I previously mentioned account lockouts though the OWASP guide seems less favorable on them due to the fact that they can be used in a DOS. The did mention delaying the response on repeated failed attempts.

The guide goes on to say:

Although brute-force attacks are difficult to stop completely, they are easy to detect because each failed login attempt records an HTTP 401 status code in your Web server logs. It is important to monitor your log files for brute-force attacks-in particular, the intermingled 200 status codes that mean the attacker found a valid password.

While nobody wants their users to be hacked, recognizing and responding to a successful password attack will dramatically reduce the cost of the attack. Monitoring can also allow you to (temporarily) lockout IP addresses to terminate malicious users (though a strong attack will likely come from many IP addresses making this hard to do).

Two-factor authentication reduces the impact of a stolen password.

Prevent our attacker from using our Web Services API to probe our server for other attack possibilities.

Sorry but you can't really do this without physical security. You can embed secrets in your app, obfuscate it, etc... but none of that really makes the app more secure, it just raises the cost of an attack as those can be backward engineered. So whether you implement those strategies or not, you must still design your API as if an attacker has access to your secrets and code.

I'm a big fan of both SAST and DAST. Run static analyses and resolve all high/critical issues before every deployment. The same for a dynamic scan. Vulnerability scanners can also check your site for known vulnerabilities and bad configuration.

Neil Smithline
  • 14,621
  • 4
  • 38
  • 55
  • 2
    You have nailed my concerns. Thank you. We already do the usual things with our bot/DDos provider, exponential back-off, etc. We can't do CAPTCHA on the Web Services side, but we do on the browser API. We're using encryption/HMAC as our means of creating a robust and secure API. It's becoming clear that I am missing the point! So: Are there any recommendations or resources available for creating that robust and secure API which can withstand examination by a would-be attacker? – Edward Barnard Sep 12 '15 at 22:43
  • 2
    I know of no single resource. The [OWASP REST Cheat Sheet](https://www.owasp.org/index.php/REST_Security_Cheat_Sheet#Anti-farming) is a good start. Google "secure API" for more. It sounds to me like you are doing a good job, so take a deep breath and just move forward. – Neil Smithline Sep 12 '15 at 22:49
  • Thank you for the encouragement! I've been an avid promoter of OWASP resources for years. I'll ask Google too! – Edward Barnard Sep 12 '15 at 22:57
  • With the edit, I'm marking this the accepted answer. Thank you! We've been a large-traffic web site for many years and have admins good at dealing with the usual "script kiddie" headaches. The Web Services are a much-less-understood attack vector. We have some people getting a very rapid education :) – Edward Barnard Sep 13 '15 at 03:11
  • @EdwardBarnard could you share any of the resources you ended up using to become more educated and successfully create this secure webservice/api? – james Sep 16 '16 at 14:55
  • 1
    @binnyb I wrote a 3-part magazine article, first part contains my suggested-reading list at the end, first part can be read free. https://www.phparch.com/magazine/2016-2/may/ My approach is mainly based on the excellent discussions here (thanks everyone!). – Edward Barnard Sep 16 '16 at 19:33
3

Your problem still doesn't make a whole lot of sense.

It sounds like you're trying to protect user accounts from enumeration and brute forcing passwords. This has nothing to do with your client.

To protect against brute-force password attacks, see https://stackoverflow.com/questions/30369529/block-request-for-multiple-unsuccessful-logins-for-a-period-of-time/30382110#30382110

To protect against username enumeration, Return the same "success" message whether the username exists or not for registration or password resets.

Please clearly indicate the actual problem you want to solve, without offering any potential solutions, and we can help you out better.

Neil McGuigan
  • 3,379
  • 1
  • 16
  • 20
  • We already do the things described for brute force attacks. We have a DDoS/bot provider. CAPTCHA does not help because our app IS a bot, in effect. Counting failed attempts by user does not really help when our attacker is running a list of hundreds of thousands of different user/password combinations. We can shut down specific groups of IP addresses, etc., etc. I **think** the real issue I'm getting at is how to tell the difference between our app and the attacker. – Edward Barnard Sep 12 '15 at 22:33
  • 1
    Can you count failed attempts on IP address? – MattP Sep 12 '15 at 22:47
  • I edited the original post to restate the question now that I better understand what I'm trying to solve. @MattP That indeed is our first point of detection. It can be circumvented by an attacker, to be sure, depending on how sophisticated is the attacker. – Edward Barnard Sep 12 '15 at 22:54
  • 1
    Like my linked answer says, if you don't like captcha, use proof of work – Neil McGuigan Sep 12 '15 at 23:25
  • 1
    Also in my linked answer, you can count attempts by password and not username – Neil McGuigan Sep 12 '15 at 23:28
  • @NeilMcGuigan thank you. There may be a way we can have our app prove a unit of work. Counts by password is an interesting idea. I had glossed over that. – Edward Barnard Sep 13 '15 at 03:08
  • @MattP you can count by IP address but a sophisticated attacker will use an anonymizing proxy, which will supply a new IP address *every request* – Neil McGuigan Oct 09 '15 at 07:00
2

Your attacker is able to sniff the app traffic because he must be using some proxy tool such as fiddler. That's not an issue with your application. This is how these proxy tools work. As an App developer you should know that and also you should implement security controls accordingly. If you want to avoid this, you might want to use certificate pinning but that is also not foolproof. You should always assume a worst case scenario and develop your code. Put all security controls and business logic at Server Side and do not store any sensitive information at client side.

Pankaj
  • 21
  • 1
1

Certificate Pinning is another commonly used control against Data-in-transit attacks. The TLS handshake never starts if the client doesn't receive a Public Key that is within the client's (the mobile app) known Pin List.

I used Cert Pinning as a compliment to payload encryption. Perhaps the following are becoming the base standard for sensitive mobile app data? It looks like Tweets and Facebook Status Updates use all three:

  1. Certificate Pinning
  2. Payload Encryption
  3. TLS

All of these are can be defeated. But are we not in a game of layers of defense & slowing down the attacker?

You summarized the problem perfectly; how do you know the mobile app is there? It could be a tool like Postman or Paw automating requests. That defeats everything if the Payload Encryption secret key is known.