ORIGINAL REDDIT POST

Securing a public endpoint

I'm building an API that acts as a middle layer between an external API and my frontend. It exposes several endpoints that are used internally, but there's one public endpoint that is intended to receive requests exclusively from the external API. Right now,…

Original postr/webdev

I'm building an API that acts as a middle layer between an external API and my frontend. It exposes several endpoints that are used internally, but there's one public endpoint that is intended to receive requests exclusively from the external API. Right now, I authenticate incoming requests using JWTs. This means that any request can still reach my backend, and only after my application processes it do I reject it if the JWT is invalid. While this prevents unauthorized access, it doesn't protect my infrastructure from malicious traffic. An attacker could still send a large volume of requests, forcing my server to spend CPU, memory, and network resources validating and rejecting them. I'd like to add another layer of protection so that requests which are clearly not coming from the external API are dropped as early as possible, ideally before they ever reach my application. My goal is to reduce the impact of potential ddos or abuse and avoid wasting backend resources. One important constraint is that I can't rate limit the external API. It must be able to send as many legitimate requests as needed without being throttled. My first idea was to whitelist the external API's source IP addresses so that my load balancer would only accept traffic from those IPs. That would be the simplest and cleanest solution, but unfortunately the external API doesn't use a fixed set of IP addresses, so IP allowlisting isn't an option. The next idea I had was to put an API gateway in front of the service. I'm using GCP, which has a managed API Gateway, and I could have it validate the JWTs before forwarding requests to my backend. That way, invalid requests would never reach my application. However, deploying an API Gateway solely for JWT validation feels like overkill. I'm curious how you would approach this problem.

Collected discussion

12 comments

u/royallifr

You should give a try to Cloudflare, In your backend allow only the cloudflare IPs, so the cloudflare will handle the abuse for you.

u/omry8880OP

Thank you for the detailed answer. It's public in the sense that everyone that knows the url can access it. I don't advertise this endpoint anywhere. It's supposed to be used only by the external API. Nothing in the documentation is written regarding IPs. I have reached out to them and waiting for an answer. The only authentication they provide is an authorization header with a JWT bearer on their requests. No, I haven't, and you're completely right. I'll make sure to do bench marking straight away and see if it really is a problem. I assume that with the number of requests going up it always will eventually be a problem? E.g. even if for 10k not-authenticated rps bench marking would prove to be alright, for 100k it prob wouldn't. And if for 100k it would be alright then for 1M it wouldn't. Isn't it better to be safe than sorry?

u/omry8880OP

Will check it out, thanks!

u/omry8880OP

Thanks, I'll make sure to read about it.

u/omry8880OP

Thanks for the detailed answer! Unfortunately I don't control and can't make changes to this external API, so I can't use mTLS or add the secret header. I'll make sure to read what you sent!

u/omry8880OP

Yes, this is what we use today.

u/Gremlation

The only authentication they provide is an authorization header with a JWT bearer on their requests. Right, so what's the problem? That's super cheap to reject. You don't have a performance problem to solve. They already considered this problem and already gave you the solution, so now all you need to do is use the solution they provided to you. Isn't it better to be safe than sorry? No. Validating a JWT is super cheap. That's the entire point of them - they are stateless; you don't have to hit the database or any external service at all. What you are missing is the cost of doing this. Firstly, the opportunity cost. You're spending time / effort / attention on this that could be spent doing something that's actually useful. Because you are focused on this, you are not doing something else. Secondly, the complexity cost. All other things being equal, simpler is better. You are introducing complexity for no reason, which makes your codebase worse.

u/Mediocre-Subject4867

sign up to cloudflare and setup some simple rules to intercept the unwanted traffic. It's all possible on the free tier. You can block ips, countries, url patterns etc

u/binkstagram

This, plus also look at whether some of your responses are cacheable at the CDN level with Cloudflare

u/ribtoks

All public APIs are rate-limited in some way (e.g. small rate limit without authorization, larger rate limit with authorization). Including AWS, GCP, Azure and all the rest you can imagine. It is unlikely that you have such a requirements that rate-limit would do any harm. Also, it is only reasonable to expect that your server is not infinitely capable so there should also be some kind of per-server rate-limit too (it exists anyways, whether you are aware of it or not: if only on the level of socket descriptors in Linux, a default values of reverse proxy if any etc. etc. - so it's better to introduce some control over it as any external API has some kind of contract). As another small trick, you can pass a small "secret" header (not jwt) and filter out requests that do not have it - if you control the external API in any way. There's also a mTLS option and a Wireguard option (e.g. your "public" API is public only inside a given Wireguard network). It's unclear what to advice to you because it's unclear to me other requirements of your "external" API (that so far what I read you want to make look like external, but it rather be very much "internal" - so only _your_ services can access it). You might also get some more ideas from https://developers.cloudflare.com/fundamentals/security/protect-your-origin-server/

u/Gremlation

there's one public endpoint that is intended to receive requests exclusively from the external API. Is it really a public endpoint? From the rest of your post it seems like this is a private endpoint. You don't want the general public to be able to access it, right? What do the people running the external API say about it? Normally there's already a solution in place for things like this. e.g. you say that it doesn't use fixed IP addresses, but did you read the documentation or just assume based on the traffic you saw? They might all be within a specific range. Do they provide client certs? Do they use signed requests? You seem to be approaching this as if you have to invent some new solution when most of the time all you need to do is look at the documentation to see how it is designed to be used. You aren't the very first users of this API, right? Authentication isn't some obscure thing specific to your application, right? Somebody will have done this before and written down how. However, deploying an API Gateway solely for JWT validation feels like overkill. Using an API gateway for authentication is one of their primary use cases. An attacker could still send a large volume of requests, forcing my server to spend CPU, memory, and network resources validating and rejecting them. Is this actually a problem? It's very cheap to reject unauthorised requests. Have you benchmarked it and found it to be a problem? Or are you spending time working on a problem you don't have when you could be working on something that has actual value?

u/KabouterKaasplank

Does the external API offer the option to set an authorization header? Or query parameters? You could pass along a token in there to keep unwanted callers out.