13

I'm designing a JSON web API and want to distinguish clients by unique IDs, in order to monitor usage and block malicious/misbehaving clients. The API is NOT encapsulated in a JavaScript library and NOT exclusive to web apps, any client type can use it (desktop, phone, etc.).

The problem is, that the web app (official website) is also a client of the API itself, thus would have to expose its API key. As a result, some user could just extract the key from the JavaScript on the page and use it, instead of generating his own key.

Is it possible to mitigate this problem somehow with some better/smarter design choices, or do I have to live with the fact that anyone using the API in bad faith can exploit this?

I have 100% control over the frontend app (EmberJS) and backend servers (Go), so any alternation can be suggested.

  • I'm using rate limiting per session/ip to add an extra protection layer for that case
  • The twitter.com page was once also a client of its own API. How did they solve that?

Note: The question is not about authentication or security itself, but how to require 3rd party users to use an API key in addition (!) to authentication!

  • 1
    This is a pretty close duplicate of http://stackoverflow.com/questions/2256305/how-does-google-maps-secure-their-api-key-how-to-make-something-similar. Some good answers in there. – Justin Anderson Jan 23 '13 at 02:31
  • Unfortunately it's not as easy as I thought. The answers provided there are assuming that the app is only used in the browser loading the Google JS lib. That is not the case with me. So more specific answers are still appreciated! –  Jan 23 '13 at 03:13
  • No, I cannot require a domain (resp. check for correct domain in JS), since the API is not exclusively for web-apps, but also desktop clients, phones etc. –  Jan 25 '13 at 07:13
  • @ErikAigner could you please throw some light on what the API does. would like to think if its possible to have a secret key along with the API key. – Mehavel Jan 25 '13 at 10:14
  • Does your official webapp access your API only directly from JS? Is it a requirement that it does? Can you rework it so that you access your API only from your server-side code? Usually when you want to avoid an unauthorized API client from impersonating a legitimate one, you would want to use some method of signing your requests with an API secret, which however would require that you do it server-side, because otherwise you would need to also expose your API secret in your JS code. – lanzz Jan 26 '13 at 22:12
  • The web app is written in EmberJS, so basically its 100% JS. But I've 100% control over frontend and backend architecture, so I could incorporate some changes. –  Jan 27 '13 at 01:47

3 Answers3

5

You should distinguish between web and non-web clients. An access key for web cannot be used in non-web and vice-versa. For web clients, you can do referer checking etc. You could also dynamically create access keys for your application and automatically change them daily (or every session). You can also add some special verification for your app only, e.g. some additional key that is calculted by obfuscated JS.

Nothing can prevent a malicious user to emulate a browser, execute the JS, manipulate that, and then do bad things - but you can make it annoying enough that they decide it's not worth their effort. Really important things like permissions etc. obviously need to be checked server-side, so abusing your API should not be much of a problem. You will have to treat API abuse via your site's API key the same as you do with regular web app abuse - IP blocks etc.

You still need to keep API keys for non-web clients secret. This can only be done unreliably by obfuscation, which you can leave at the hands of the client developer. If their key gets leaked and abused, you revoke it, and they will be motivated to fix it.

Have a look at OAuth 2.0, they impelement many features that could be useful for you. Even if you don't want to use it, you can take some inspiration from it. OpenStreetMap uses OAuth (not sure if 1 or 2) for their flash-based editor; as long as it is called from the same origin by a logged-in user, the OAuth permission granting is done automatically. For third-party apps, the user needs to do it manually. You may want to check that out.

Community
  • 1
  • 1
Jan Schejbal
  • 4,000
  • 19
  • 40
  • 1
    I thought about some of those points before. 1) Referrer can be easily spoofed 2) If my app can retrieve new credentials on a daily basis or so, anyone else can too. 3) Obfuscation only takes a little more time, but is no barrier. I do check permissions and rate limit per ip/session anyway. –  Jan 29 '13 at 06:39
  • 1) I know of no way to spoof the referer if you are a web site in a browser you don't control. (With control over the client, it obviously is.) 2) Your application gets loaded from your server. Automatically changing the credentials, re-ordering the variables, then re-obfuscating the files (e.g. with the Closure compiler) is easy. Automatically extracting the correct var is possible, sure, but non-trivial and might be enough to make attackers decide it's not worth it. *You can't get perfect protection.* 3) Obfuscation is very good at making prying the key out very annoying, i.e. not worth it. – Jan Schejbal Jan 29 '13 at 12:55
  • ad 1) See question: "The API is NOT ... exclusive to web apps" –  Jan 29 '13 at 13:04
  • @ErikAigner: This is why I suggest to use separate keys for web apps and non-web apps. If a non-web app decides to use a web app key, it will have to spoof the referer and whatever "is this a real browser" checks you implement. Again, there is no way to achieve complete protection. The attacker could open your web app in a modified browser and change arbitrary parts of the JS, even if you somehow ensured only your page can access the API. I doubt impersonating your web client on his own machine gives an attacker any advantage. – Jan Schejbal Jan 29 '13 at 14:22
  • (cont.) Unless you have a real reason to believe an attacker has a reason do try this (then you may need to rework the security model anyways - you can not fully trust the client), my suggestion would be to have the key separation in web/non-web there, but only implement further countermeasures when you actually encounter attacks (which will probably never happen). – Jan Schejbal Jan 29 '13 at 14:23
5

You will not be able to make your API secure just using a single API key. The API key you are describing is basically a public key and you will need some type of private key for secure identification/authentication and a mechanism to deliver it.

You asked how Twitter got around this issue. They use Oath 1.0a. Here is brief description of how it is tied to the API key from a Twitter Developer FAQ.

Most integrations with the API will require you to identify your application to Twitter by way of an API key. On the Twitter platform, the term "API key" usually refers to what's called an OAuth consumer key. This string identifies your application when making requests to the API. In OAuth 1.0a, your "API keys" might refer to the combination of this consumer key and the "consumer secret," a string that used to securely "sign" your requests to Twitter. Most requests to Twitter require a user context in addition to the application context. User context is presented through the use of another kind of token/key called the "access token." See Obtaining access tokens for more information.

You can find a lot of great resources on designing API's at Apigee.com. They recommend using OAuth 2.0 for authentication/authorization.

Here is a description on how to use HMAC authentication to secure a Web API.

I have used a workaround for my web application when I have had to use API's that only used an API Key. I do not access the API directly from the client-side portion of the web application (i.e. JavaScript in the web browser). Instead I access the API server-side and store the API Key encrypted in a secure configuration file. I provide a Facade to the original API and use my own security methods to secure the Facade API that are dependent on the type of application.

Community
  • 1
  • 1
Kevin Junghans
  • 17,475
  • 4
  • 45
  • 62
  • `HMAC`/`OAuth` won't help when the JS app is a client. You mentioned "and use my own security methods to secure the Facade API". That part would actually be interesting. What are your security methods? –  Jan 29 '13 at 16:22
  • Use OAuth, Forms Authentication, or Basic Authentication for authentication of user of web application and then use cookies or tokens that expire between the web client and Facade API. – Kevin Junghans Jan 30 '13 at 13:54
  • The question was not about authentication, but requiring users to use an api key **in addition to authentication**. –  Jan 30 '13 at 14:02
  • What are you doing with api key if it is not for authentication? Aren't you using the api key to identify who is using your api? I offered the alternative of using cookies and tokens instead of api keys. You can continue to use an api key for your api but there is no way to make that secure in javascript. Specifically your question is, "..., or do I have to live with the fact that anyone using the API in bad faith can exploit this?" The answer is yes, if you just use an api key. – Kevin Junghans Jan 30 '13 at 14:41
  • No, the api key is just to check which user is using which client - not for authentication itself. I just want to require 3rd party users to use an API key in addition to auth. –  Jan 30 '13 at 14:43
  • @ErikAigner "Phone and desktop" clients, are they 3rd party applications or something that you develop as well? I'm guessing in case of abusive/malicious activities you want to block individual users on phone/desktop/web-client not the whole app, right? If so, then authentication and API authorization must be considered together. OAuth does seem like a good fit here. https://developers.google.com/accounts/docs/OAuth2 – pavelgj Jan 31 '13 at 22:53
-2

General API workflow:

  1. client sends a request
  2. request is authenticated and authorized
  3. data is sent back

Web Site - log in

  1. user logs in providing a username and password
  2. A secret is created and stored into a coockie

Web site - API access

  1. client sends a request
  2. request is authenticated and authorized based on coockie's secret (coockies are sent with the request)
  3. data is sent back

Non WEB client - obtaining an API KEY (long random alpha numeric string)

  1. Option 1 - User registers a client on the website, gets an API KEY and stores it into the client
  2. Option 2 - User enters username and password into the client, the client requests an API KEY with usenamer and password, the key is returned and stored into the client. Username and Password are not stored on the client.

Non WEB client - API comunication

  1. client sends a request with API KEY
  2. request is authenticated and authorized based on API-KEY
  3. data is sent back

When the key is generated with option 2 you can get some additional data since it originates from the client (operating system, browser). In this case when checking the API-KEY you can force a user to generate a new one if, he changed OS or Browser

The key point is that your API authenticates requests in two ways. Using coockie's secret or an API KEY. Therefore there is no need to expose the API-KEY on the website.

Note that for clients using an API-KEY there is no session involved. Each request is authenticated only by the API-KEY. So those clients are considered non WEB app.

Andreyy
  • 511
  • 2
  • 11
  • This does not address what was actually asked in the question. –  Jan 29 '13 at 11:47
  • Wasn't the question how to avoid exposing API-KEYs on the website ? – Andreyy Jan 29 '13 at 11:53
  • This is just an explanation of session based authentication, which I do anyway. Nothing in your answer forces someone to obtain an API key or how to distinguish them from the regular web app/client (which was the question). –  Jan 29 '13 at 11:58
  • The web app doesn't need an API key since session based authentication grants access to the API. For other clients you can't access the API unless you provide an API key. There is no session involved. Requests non session authenticated and with an API-KEY are considered non web app. – Andreyy Jan 29 '13 at 12:25
  • And what prevent's someone from just using the session based auth instead of using an API key? If the problem was that trivial, I wouldn't shell out 250 points for it... –  Jan 29 '13 at 12:47
  • Nothing. You can always emulate a browser and you can't do nothing about it. Session based auth is possible only by providing username and password. And in the process the API-KEY is never exposed since it's not needed. – Andreyy Jan 29 '13 at 12:54