| I wouldn't say I have a solid security background, but there are four best-practices I can think of that would have prevented the security vulnerabilities outlined by this post: 1. Hash the secret API token/key given to each client that is sent to the server with each API request. This will prevent attackers from being able to find out your secret token. If you only hash the secret token though, this still won't help, as attackers could just send other API requests along with the hashed token. Instead, you will want the client to hash other data unique to the specific API request as well. For example: http://api.example.com/api_function_name?app_id=my_app&hash={hash_value}&other=stuff
Where the hash_value is computed by the client with something like:EDIT: Clarified hash_function parameters, thanks @eru. hash_function( secret_key, api_function_name)
The API server will then receive the request, look up the client's secret_key based on the app_id, and run the same hash_function to make sure it matches hash_value in the request.This would mean attackers couldn't reuse the hashed value to send other API calls. But they'd still be able to send calls to that function and just change the parameters to the call to get other information from that API call. So, you could also include the other API call parameters in the hash_function as well, which would mean attackers could only replay that exact API call and not change any of the parameters. You might notice, this is still not good. So, to prevent this "replay attack", you would also generally include the current datetime in the API call as well: hash_function( secret_key, api_function_name, datetime )
Now, attackers can't even replay the exact request, because by the time they do, the datetime will have changed and so the API server would reject the request if it was replayed later. And since the attacker still doesn't know the unhashed secret_key (since it's never been transmitted in plain text), they can't change the datetime without invalidating the hash_value.This is theoretical though, because in reality the above wouldn't work well if the clocks on the client and server were at all out of sync (and they probably will be). So, usually, you'd also have to include the current datetime of the request as another parameter in the call to let the server know exactly what datetimestamp was used in the hash_function, and the server will simply make sure the datetime is within an acceptable window of the current datetime on the server. Of course, the bigger the window, the easier to get the API working with clients, but the larger the window for allowing replay attacks. http://api.example.com/api_function_name?app_id=my_app&hash={hash_value}&other=stuff&time=datetime
And lastly, the chosen hash_function for the API should be something not easy to brute-force (meaning don't make it easy for attackers to listen to a few API calls and be able to reverse-engineer the secret_key, since they'll already know what hash_function is from the API documentation).OK, 1 was longer than I anticipated, but the others are pretty short. 2. Another more full-proof way to prevent attackers from getting secret tokens, hashed or unhashed, would be to make all API requests work only via HTTPS. 3. Don't provide API (or any) access to user passwords. 4. Don't store user passwords in plain-text, or even via simple hashes. Instead use a cryptographically secure hashing function with salts. |
Use OAuth or similar and make sure every user has their own account. That's the only answer. Don't roll your own! Especially don't roll your own when you don't have a solid security background. You have obviously heard some of the right terms, but how and where you can apply them is at least as important as using them at all.
1. Doesn't protect anything at all. Hash functions don't do anything when people have access to the program code. No matter how fancy you go with time limited hashes (and there are smarter ways to create those). At most it adds a few minutes to the reverse engineering.
2. Again, this doesn't protect against anything. HTTPS stops intercepts on the wire, not someone who has access to your app, people can still lift the secret keys and the hashing scheme from the app binary.
3.& 4. Both true, and would protect against mass stealing of the passwords like happened here, but it wouldn't prevent abuse of the API.
There are very many people who use techniques like the ones you suggest in 1. and 2. and the same very many people have vulnerable apps that usually expose all users' data to the world. There are a lot of apps that store e.g. user files or some sort of configuration not on a per user OAuth protected storage like OneDrive, DropBox or Google Drive, but either there but on just the account of the developer, or on another storage that is only authenticated with the developer's credentials. People who do that allow anyone to read and modify the data of all users, exactly the same as people are allowed to do to their own data, or more if the credentials aren't properly limited, even if it's blocked in the app.