I have a mobile app (currently IOS and soon Android) which talks to a web service. There is no login and the data is not private. Basically, the app POSTs a marker (lon, lat
There is nothing really you can do on the client side. You have to give the whole app (including any keys or any other protection mechanism) to your user. If a malicious user wants to play mischief with your web service he will have to do some reverse engineering to get to your web service. You can make this harder, but you cannot prevent this, no matter how hard you try.
I’d just implement some server-side rate limiting (per IP address) and not worry any more about this. This is a battle you cannot win. If someone really wants to hurt your server he could just DDOS it without knowing anything about your web service protocol.
Also, generating an unique key or certificate per user automatically on first connect does not help at all. After an attacker reverse-engineered your protocol he knows how to handle all this and doesn't have to play by your rules. Nothing would stop him from requesting a new key from your server every time he runs into the rate limiting.
The approach Kuba Wyrostek described could work - make the client perform some time-consuming computation you can quickly check before you allow the request to be processed. But this can't take too long or your users will complain about the reduced battery life. Also an attacker will probably use more powerful desktop hardware instead of another iPhone.
And a last point - do you really think this is necessary? You don’t want your users to have to register, so your data or service can’t be too important. So what would anyone have to gain from reverse-engineering your app and flooding your server with requests?
I've heard about this idea once, when talking about finding a global solution to SPAM problem: force your client to perform some time-taking computation.
To be precise: find some computational algorithm, that can compute some z for a pair of x and y in a blink of an eye, but it takes some considerable amount of time to compute z being given only x. I can not provide actual algorithm but I am sure that there are plenty of them that would much this criteria.
Now the whole procedure should look as follows:
session_id and for this session_id a pair of x and y.session_id and x.session_id and calculated z.z is all right, for you already have x and y that let you easily do it.session_id store how much/often it is being requested. The moment you suspect it is being abused - force regenerating x and y.x and y upon each consecutive request for a session_id.Choosing between 6 and 7 is actually tweaking that depends on the complexity of algorithm vs. expected 'fair' use of marker database. If your estimates are good - the evil client should never obtain too much data or overload your server.
Hope it helps.
I've actually been looking for a reason to implement a few of these ideas. Great question and answers so far.
I agree with @Kuba Wyrostek regarding treating it like a spam problem is part of the solution. Especially if your app will have textual messages (adding a store, service, or message), you may find that a common reason to spam your app would be to advertise something. That would lead to my first recommendation:
1) Treat each message's validity as a percentage from 0% to 100% valid. Develop a process on the server side to with heurestics to mark the message as more or less valid. This will allow you to target some of the additional methods (such as forcing a client to calculate a complex value) to only those requests where it is needed. You can also more easily log and review possible abuse (and more easily clean out that abuse after it is targeted).
Your apps do have a strong advantage over email servers in the spam war, however - you control both sides of the conversation. This situation actually reminds me of two other related situations that you might find helpful: the satellite Pay-TV "wars" and Instant Messenger clone "wars". (Reference Jeff Atwoods post on the Black Sunday hack, for example). Here are a few ideas from those standoffs that might help you get a little ahead of the game of cat and mouse:
2) Require the client to send extra data - as much data about the request as makes sense. On iOS, send the accuracy metrics for the location. On Android, you can actually get raw GPS data such as ephemeris information. You can then (maybe not right away, but later), start checking this data for validity. This forces someone reverse engineering the requests to work that much harder. If they send the GPS satellites in view, for example, you could check that against publicly known data to confirm.
3) Force your adversary onto the mobile device - As @Sven notes, your attacker might use a desktop PC, which means a "computationally expensive" request might become trivial. Don't let them do this (or at least make them work harder). You can, for example, have the client compute some mathematical function (sent by the server), and see, based on the phone model, if it takes the correct number of milliseconds to complete. Or do a small 3D rendering task with data from the server, that relies on hardware clipping behavior. Hash the result and send it back. All of these would be within a range - it is a multitasking OS. But it would help tremendously.
4) Go dynamic on them - Send along bits of algorithm that need to be computed in the client's context. Apple gets a little funny about remote code to be interpreted, but something like sending a bit of javascript that does not render to the user might work. That code could ask all sorts of unique questions (screen resolution, browser version, WebKit quirks) that would be hard to anticipate up front. As they catch up, you can get more creative with these.
5) CAPTCHA - If your heuristics start seeing suspect data, force them to authenticate. If you have a multilingual app, it could be as simple as matching a picture or unicode character to another one. Render it in a way that you can update it later.
Anyhow - a few additional ideas. Good luck!
Here is another "solution":
because:
if there are suspicious performance or query spikes go for the least time consuming solution:
this will in 99,99% solve your problems and you can get to work right now and write awesome new features.
You could use rate limit + client 'soft' registration.
Basically you would generate a device ID that you could store in the user defaults upon the first request. For every request you track how many request have been sent to the server and limit it server side. This can be achieved really quickly.
You can also have some kind of shared secret used to sign your request with the generated device id + post/get parameters
Actually in your particular case, since it is currently an iOS only app, there is a solution.
After the user downloads and runs the app for the first time, the app hits a /access_token/create API that comes up with a GUID and relays it back to the Application via Apple's Push Notifications.
App stores this access_token, and uses it on all subsequent requests. Your actual API's can rate limit on the basis of the access_token.
Basically, you let Apple do all the hard work of ensuring that the initial request came from an actual iOS device.
Extending this to Desktop clients is possible, but somewhat ruins the UX. Just change step 1 to allow /access_token/create to accept arbitrary requests, and if the request is not from a iOS device, then force the user to verify their email address/solve a captcha etc before issuing them an access_token.
Android devices (not really familiar with them) may have a similar Push Notification mechanism, in which case you can use that, or may not have a Push Notification mechanism, in which case you can subject your Android users to the inconvenience listed above.