How to secure apis that contain public data?

Not applicable

Hi,

We have a couple of APIs that contain public data. These APIs are using oauth2 client credentials to secure them. The main benefit behind using oauth2 is to limit any potential abuse of the data and make sure that people are not scraping this public data. We have a couple of developers that want to write single page apps / javascript apps and call these public APIs.

I'm really struggling figuring out a way to keep the APIs secure. I know that with javascript somebody could just view the source code and get any credentials that are in it. A few thoughts that came through my mind are:

* Have a server side page that makes the api calls on behalf of the javascript app and just returns html data. The developers are hoping to not have to put much load in their servers. That's why they were looking at a server side page that would make the API calls. The server side page could still be abused in the sense that somebody could just hammer that page to scrape data.

* Restricting the api calls based on IP addresses. This could prevent too many calls or a spike in the number of calls from the same IP address.

* Letting the javascript code use an access token that has a very short expiration date. This would help, but somebody could easily just refresh the javascript app to get a new access token.

Are there some security mechanisms that I'm not considering? The data is public so using 3-legged oauth doesn't apply.

1 3 1,769
3 REPLIES 3

Yes, the issue you're facing is a common one.

Just dropping the credentials into a JS webpage won't work well; as you say, anyone can inspect the source and obtain the credentials.

Normally the solution is to involve some server-side logic to manage and control the credentials used to call to the API service. In this way the credentials won't "leak" because your server-side logic will be under your control, will be private.

But if a webpage is allowed to call the server-side logic, you have the same problem that you started with.

A common way to address it is to include user authentication into the webpage experience. The webpage can make calls to the server-side, but only after authenticating the user. There's a cookie set in the browser session, and then the server-side would only allow the API call if the cookie is valid, not expired, and includes user authentication.

But you said your APIs are public, which I guess means there will be no requirement to authenticate users before they call the APIs.

A similar way to make this work is to use a nonce, generated by the server side and sent to each browser, to mark the browser session. This nonce is unique, but anonymous. It does not identify the user, but it does identify the unique browser. In the server-side logic you can enforce a lifetime on the nonce, and also tie it to the original IP address of the browser client. When the single-page web app makes API calls, it must send the nonce. When the SPA sends the nonce to the server-side logic, along with the API call, the server-side logic acts as a proxy. It verifies the nonce, then if valid (correct IP and not expired), it calls the actual API.

But Wait! You HAVE something that acts as an API proxy - Apigee Edge. So .... at this point you don't need the server-side to act as a proxy. Just need it to issue a Nonce. The SPA can send API requests with the nonce to the Apigee Edge proxy. The Edge proxy verifies the nonce, somehow calling the server-side logic, or reading a database that is shared between the server-side logic and Edge. If the nonce is valid, and not expired, then Edge will allow the call.

But Wait! Why do you need the server-side logic at all, then? At this point you could have the SPA just request a nonce directly from Edge, when the page loads. It should last for the duration of the browser session. That request can go directly to a token dispensing endpoint you configure on Apigee Edge. Edge can use fixed client credentials, which are stored in Edge itself, not in the browser JS code. But there needs to be a rate limit on the requesting-of-nonces, so that only a small number of requests-for-token are allowed per IP Address for a given time frame. Let's say 4 per hour. And the nonce can last for the length of time that 80% of web sessions last; maybe that is 45 minutes in your case. Maybe less. Apigee Edge can generate a token if the rate limit passes, and attach the inbound client_ip as a custom attribute to the token. Set the expiry to 45 minutes. Return the generated token to the browser as the "nonce".

The browser session then just sends back the nonce (token) with each subsequent API request. Edge will call VerifyAccessToken, and then can check the IP address against the custom attribute. If it's valid, then Edge would enforce a quota on the API request, using the token as the identifier. You can have a multi-level quota as well, using just the IP address as an identifier (to prevent multiple browser sessions (perhaps automated) all being run from the same machine).

In the browser SPA, you can use JS setTimeout() to trigger a call to a refresh API, in case the browser session lasts longer than 45 minutes (or whatever you set the token lifetime to be). In which case you will have configured Edge to invalidate old tokens and issue a new one, good for another 45 minutes.

If the user enters and leaves the page too many times, the browser request for token will be denied based on the rate limit. You can have the SPA JS pop a notice to the user, "you're doing that too much!"

The "public" API can also be exposed in a separate way, for public (non browser) clients. That public API might just use never-expiring API keys, against which you can enforce a quota. So, two different ways to access the same backend. Two different proxies in Edge. Plus one proxy endpoint for the oauth token dispensing.

Basically you are building two ingress points, two proxy endpoints, to satisfy 2 different use cases.

The first is some apparatus to support API calls coming from the single-page web apps.

The second is an easy-to-use API for non-webapp clients, in which the apikey can remain a secret.

Thanks for the detailed answer. Just to make sure I understand your answer, I'll try to summarize based on my understanding. You are saying to use a new apigee proxy endpoint for javascript or SPA where credentials would usually be exposed by viewing the source. This new endpoint would have have an initial step of issuing a nonce (token) that is tied to an ip address. The SPA would then use the nonce (token) to issue the api request instead of the access token from oauth2 client credentials.

For this nonce issuing / verification part of the proxy, could the oauth2 or another built-in apigee policy be used? Having to store & expire nonce (tokens) by using code could be extra code that I'd rather not have to maintain over time. In your suggestion of using an apigee proxy that issues / validates nonce (tokens) based on the API, wouldn't you have to use the client_id in the request? Specifying the client_id in the first nonce request is the only way to verify the developer's / app's access to the API. What security concerns would be associated to this?

What is described is not a nonce. It's a kind of an access token, but not a nonce.

By definition, nonce [number-once] semantics implies "expiration" by consumption, not by time. 

For a true nonce implementation, server needs to generate and store nonce value. client needs to save a value from a previous response then send it with the next request. Server then checks it and if compare, proceed with request and generate a new nonce. Then send it back. And so on.