Easy ways to hide API keys

@Avery
Absolutely true; CORS is the way to go—it was a long day :sweat_smile:.

Quinlan said:
@Avery
Absolutely true; CORS is the way to go—it was a long day :sweat_smile:.

CORS won’t help you because I can just use curl or write a script to call your backend service directly, which doesn’t care about CORS.

If you let anyone access this page without knowing in advance who is going to access (or without charging for access), then pretty much any method you set up can be bypassed.

You can rate-limit by IP, but then I can use a pool of IPs. You can make people sign up and give per-account rate limits, but then I can script the creation of multiple accounts; you need to detect that and rate-limit too.

OP needs spending limits set up, and overall rate limits on API key use, in addition to per-IP and per-account rate-limiting. They also need appropriate monitoring on the backend to detect and respond to abuse.

How far you need to go depends on how valuable that service is (how badly would malicious actors want to abuse it?) and how much money you stand to lose. People will do anything to get unlimited access to cat facts.

Rowan said:

Rylan said:
@Avery
Yeah, you can use CORS for this—no need to mess with IP.

CORS is only for browsers; you can still use Postman or similar tools.

But remember that CORS only works in the browser. Anyone could still hit your API using curl or other programs outside the browser.

@Tobi
That’s exactly what I’m thinking.

My gut instinct is that a one-time password or a salted encrypted key in the client would work. Huh, but that itself might require a server to generate, which puts us back at square one.

@Tobi
But then you could again restrict access to certain IPs.

Quinlan said:
@Tobi
But then you could again restrict access to certain IPs.

You couldn’t do that because then you would have to whitelist everyone’s IP who visits your website. Maybe you’re thinking of restricting by domain, but again that can be easily bypassed using curl, etc. Someone will always be able to hit your API; your only defense is implementing authentication and anti-abuse systems like rate limiting and caching.

@Cody
Now you have to deal with users calling your middleman. This can sometimes be a simple solve. For example, if you only need to call the API for logged-in users, you can rate limit by User ID. If you need to call the API for logged-out users, then it can be a very hard problem to solve.

@Cody
This is the way. Half of all my dev work involves setting up API proxies that send requests to the “real” services, while the frontend is only aware of my single service.

It sounds like you basically have it figured out.

  1. Your frontend makes a request to your backend (standard public GET request).
  2. Your backend receives that request and in turn makes its own request to the API service to fetch the data.
  3. The data is returned to your backend and then your backend returns that data in a response to your frontend.

In terms of how you implement the backend part, there are tons of options. I’m guessing a Node server running Express.js would be easiest for you to get to grips with? But it could be a PHP app or anything.

@Vanya
And to be clear, compared to the absolutely unsafe practice of exposing the key to clients directly, this is not really simple at all. You now have backend scalability concerns that you didn’t have to manage on your potentially static site, and you’re paying for proxy bandwidth to that third-party service. To add insult to injury, if you design your backend poorly, it might still end up being an attractive vector for people to steal your API access. Ideally, you should build things like (from easiest to hardest):

  1. Limiting your backend API surface to only what is needed (so attackers can’t steal other API services).
  2. Requiring an authenticated user to hit the third-party API.
  3. Double-checking that users are only calling about themselves (if appropriate).
  4. Rate limiting users.

It’s all kind of Backend 101, but it’s often frustrating for frontend devs to discover that they have to do all this stuff.

@Dustin
And that’s when you decide that the simple 3-screen app Accounting wants should have been a website.

Farrell said:
@Dustin
And that’s when you decide that the simple 3-screen app Accounting wants should have been a website.

I mean… if it’s an internal-only app, there’s a lot more leeway for ignoring hardening best practices. In theory, every app should be 100% secure, but in practice, a company can’t spend all its money hardening intranet apps.

The standard way (for example with Google Maps API key) is to restrict the origins allowed to use the API.

But I guess not all services allow this.

Sawyer said:
The standard way (for example with Google Maps API key) is to restrict the origins allowed to use the API.

But I guess not all services allow this.

Yeah, this is my dilemma. The ones I’m trying to use don’t offer origin whitelisting, so I’m looking for a way to do that without spinning up a whole web of AWS services.

@Vale
You shouldn’t need a whole array of services. Either just a Lambda, or a simple Express server on a cheap EC2 instance will do the trick.

Sure, there are other services you can bundle it with to make it “more professional,” but start small.

Heroku would also be an option. If you’re just proxying requests, you really only need a single worker, no database.

@Cruz
Yeah, I guess you’re right.

In practice, the project I was looking at is now gonna need API Gateway, Lambda, DynamoDB, CloudFront, S3, and possibly more. BUT to your point, I’m doing more than just tucking my API keys away (orchestrating data from a couple of different APIs, so I’m not burning through requests, storing it, serving it with some basic protections). Funny how these things happen.

You can easily deploy an Express/FastAPI app to do this for you, but you’re going to have some issues that you need to address:

  1. Just because they don’t have the key there is nothing stopping someone from hitting the endpoint as if it were the API. So, you will have to rate limit / possibly ban IPs / authenticate. Any reasonable backend framework will have tools to make this easier. I know you mentioned whitelisting your client, but without going into too much detail, that isn’t going to work without implementing auth.

  2. If you’re paying for API calls, you probably want to have some sort of caching on duplicated calls. Depending on the size and amount of calls and how often the data becomes stale, this could get complex and require a database. In the best case, you can just keep it in memory for x hours.

You can get a very cheap VPS from Digital Ocean and set up your server on there; last I checked, it’s about $5 a month. You’re going to have to learn some DevOps, but these days, that’s the name of the game.

There’s no way to hide the API key in the frontend. If the key must be secured, you should have some kind of backend after all. Express has a passthrough method that can relay your request to a remote endpoint. For simple projects, you can write a single-file Express script that relays all your API requests, including payloads and cookies, to a remote API to mitigate this key security problem and CORS issues.

If this isn’t an option, but you have control over the remote API server, you could possibly make the API server authenticate the request. Obviously, using an API key isn’t an option in this case, but you can use JWT or session-based auth, or if your service doesn’t need individual user authentication, you could simply filter out the requests based on their origin and respond only to requests coming from your approved frontends.

Not sure if any other frameworks do this, but Next.js has a little neat feature where you can set up API endpoints within the same (front-end) project.

Or you can just have the top layer be server-rendered and send the results along to the client. This way, the API call is never exposed to the user.

@Dallas
Yep, this is also the case with Nuxt, and one of the reasons I love it so much. It’s so easy to use and makes life in many ways so much easier when needed.