24

I have a REST service that's reasonably complete and will be used with an iOS app. It's built using Ruby/Sinatra but I don't think that really matters here.

I'm using HTTP Basic Authentication over SSL for various endpoints and that part is working very well.

Question is: How do I stop spammers etc from calling parts of the REST service that aren't protected via HTTP Basic Authentication?

Example: User Registration

Let's assume the REST call is (POST) .../register_account passing a JSON object in the body.

For obvious reasons, this call cannot expect a username/password linked to a user account.

Ideas are:

1) The app has its own 'username' / password and some calls would check for app-credentials. Problem: Rooting the device etc could unearth those credentials.

2) The app passes a secret token via a HTTP header to the REST Service for those calls. Problem: Same as (1)

Are there any techniques commonly used out there to prevent such spam calls? I'm thinking maybe introduce the iPhone's device id in the mix but haven't identified a definite approach yet.

Thanks

Riaz
  • 423
  • 2
  • 6
  • 15

4 Answers4

8

While the app-specific code is a good idea for a first line of defense against spam, you should still implement some rate-limiting on any services you are concerned about.

For example, if you use sessions on your REST services, you can easily rate-limit the number of calls you process from a single session. The session doesn't have to be authenticated at all and is only used to identify a single client while they are making requests. A simple redirect back to the requested service if they try to connect without an open session is all that's needed, and virtually all web frameworks or stacks have this built in.

You can also rate-limit on other properties, such as IP or user-agent fingerprint, but those are less reliable than a session-based method.

cdeszaq
  • 30,869
  • 25
  • 117
  • 173
  • I will use this gem: https://github.com/datagraph/rack-throttle for rate limiting. I will subclass it so that the client identifier is a combo of device id + ip address. Will also hold on to the application credentials idea. – Riaz Jan 21 '12 at 11:26
6

In general, a common approach is the API Key, which is the same as the secret-token you describe above. You can hardcode this into your application and make it difficult for someone to reverse engineer it (hide it, build it up from various parts stored at different places within your application, etc). You are correct in that a determined attacker will be able to recover the key (if your app can do so, someone else with access to your app can as well)...but you can make it more difficult where, hopefully, it wouldn't be worth the time and effort to do so.

You could also look at deploying mutually-authenticated SSL, so that your server will only accept incoming connections from your app and your app will only communicate with your server.

Here's the high-level approach. Create a self-signed server SSL certificate and deploy on your web server. Then create a self-signed client and deploy that within your application as a resource. Configure the server to require client-side SSL authentication and to only accept the client certificate you generated. Configure the client to use that client-side certificate to identify itself and only accept the one server-side certificate you installed on your server for that part of it.

If someone/something other than your app attempts to connect to your server, the SSL connection will not be created, as the server will reject incoming SSL connections that do not present the client certificate that you have included in your app.

  • 8
    you can spend days writing the logic to "hide" the API key and spread it's parts all over your application, but at the end of the day the app makes the request and it's a matter of checking network log to see the url with the key itself. This is especially trivial in web apps by just checking "network" tab in dev tool of any browser, but it's also quite easy to do it on mobile. – vir us Jul 19 '20 at 13:44
1

This is truly old, but there is something you can do. I would create a time-based secret token. Something that expires in seconds that you can send along with the request. Since your iOS app source code isn't public, it's just up to you to keep it secret. You need to ensure that your time is synced between the app and the server. You can send the time from the mobile app to the server and calculate an offset (if there is one). This will prevent anything other than your app from talking to your API before you're authenticated.

Base the secret on something that is unique to the phone and only you would know what that is. That uniqueness would also have to be known on the server side.

Jim Berg
  • 609
  • 4
  • 7
-5

You could track ip addresses using request.ip and write some logic around that.

Christopher Scott
  • 2,383
  • 2
  • 25
  • 23
  • Wrong; there are lots of people behind NATs, thus large groups of uncorrelated requests can come from a single IP. – mbq Jan 20 '12 at 23:17
  • 2
    true. but i could combine the mobile/tablet device id and the IP in a hash to generate a unique token to compare with – Riaz Jan 21 '12 at 11:24