API Mistakes to Avoid
One of the most annoying kinds of technology-company blog posts is the one that says, “the thing that you read about in the news would never have happened if company X had just purchased our product.” In the case of Snapchat’s recent negative publicity, however, it’s hard to resist writing one of those.
We’ll do our best not to pile on, but the fact is that the Snapchat API is a real-life demonstration of what can go wrong when a few important things are overlooked. It also presents an opportunity to remind ourselves about some aspects of security that API teams often overlook.
Don’t forget about rate limits
Snapchat recently attracted some terrible publicity by enabling phishing for users' phone numbers via the API that backs their mobile apps.
Based on the excellent analysis by Gibson Security, the Snapchat app uses an API that allows the caller to input a list of phone numbers and receive a list of Snapchat accounts that match those numbers. The API had no rate limit, and furthermore it's a bulk API, so it was possible to get back the names of thousands of accounts by submitting a big list of phone numbers. The result is that attackers could theoretically bomb Snapchat with long lists of sequential numbers and build their own database of Snapchat users and their phone numbers (on second, thought, maybe this one isn’t so theoretical).
How can we learn from this? Or, more importantly, how does a typical API team avoid making this same mistake? Let’s consider a few other possibilities.
Imagine a “product catalog” API that takes as input a SKU and returns a price, or stock availability, of a particular product. Imagine that a SKU is nine digits long, and that your implementation allows that API to be called 1,000 times per second. That’s adds up to 11 days for a competitor to run through every product in your catalog and build a competitive database; this could happen even faster if the competitor breaks down the “SKU” space into a smaller set of possibilities.
Or, imagine that you have a “login” API call that takes a username and password and returns an authentication token if the username and password are correct. Armed with a dictionary and an API that an attacker can call 1,000, 100, or even 10 times per second, you have made your user API a fertile ground for password cracking.
What are some ways to limit the impact of attacks like this?
Put a “safety valve” on every API that touches the internet, no matter what. This should be simple and relatively high, such as 1,000 requests per IP address, or even per block of IP addresses. Having something that can stop an out-of-control client at least keeps a bad attack from getting worse.
Add additional rate limits based on application ID, end user, or both. These are much more targeted and can be much lower. For instance, 10 or 20 requests per second per authenticated user ought to be more than enough for a typical mobile app.
Monitor API usage and be prepared to act on what you see. Can you view your API usage by app and geography? Can you see if a certain app, or a certain instance of an app, has drastically changed its access pattern? Are you alerted when these things happen?
Don’t forget that rate limits should apply to API calls made by both authenticated and unauthenticated users. It may seem that any of your “public” API calls, which don’t require authentication, are the riskiest, but in the Snapchat case we see that API calls made by legitimate users can still be used in ways that you may not expect.
You can build these sorts of limits yourselves, you can buy a product like Apigee's or our competitors’, or you can use an open source product—but your users will thank you if you have these basic security precautions in place.
Don’t roll your own security
The Snapchat API, as documented by Gibson Security, uses a proprietary authentication scheme. Basically, each request includes an authentication token consisting of a secret hard-coded into the app, plus a “timestamp” that is hashed and rearranged in an obscure way.
This obscurified secret is used like an “API key” to uniquely identify each application. Actual user authentication is achieved by making a “login” API call that includes the password in the request body, and then returns an authentication token.
It’s hard to understand why it works this way. Why use all this hashing and string manipulation to communicate the hard-coded secret if it’s going to be encrypted using transport layer security (TLS) anyway? Why add all this extra mumbo-jumbo to identify the app and then just send the password in a request body?
But that’s not really the point. The point is that API and app developers are better off leaving security protocol design to security experts, and sticking with security protocols that have been reviewed by lots of them, over plenty of time.
Let me suggest a cautionary example. OAuth 1.0 (the original version of OAuth) is a well-regarded protocol that was designed by smart people, implemented by other smart people, and deployed to the internet. It was only after all that that some more smart people discovered that it was flawed in a pretty fundamental way. OAuth 1.0a includes the fix for that flaw, and that’s what was eventually documented by the Internet Engineering Task Force (IETF) and turned into a robust standard.
Between OAuth 2.0, OAuth 1.0a, Open ID Connect, HTTP Basic authentication, and TLS/SSL, API teams have at their disposal a set of security protocols and standards that have been combined in common ways many times for a huge number of APIs and apps. These standards should be the first—not the last—ones that API teams turn to when deciding how to secure a new API.
Don’t get complacent about your “internal API”
Lots of APIs are “internal,” meaning that they’re built by internal developers, and only used for one company’s apps. But even internal APIs run on the public Internet, and that trend is growing as smartphones have taken over the enterprise and VPNs are being used only in niche applications.
To me, this represents the biggest mistake that Snapchat made, and one that other companies are making all the time.
Once you deploy an app that uses an API, it can be sniffed, reverse-engineered, and monitored in various ways. That means that security vulnerabilities, such as a lack of rate limits, a lack of encryption, and proprietary or inadequate authentication mechanisms, are out there, waiting to be discovered.
Following well-established security practices, reviewing and monitoring your security posture, and sticking with tried-and-true security techniques are your best defenses.