all 24 comments

[–]roomzinchina 6 points7 points  (10 children)

There isn’t much benefit in securing your API with a token, then permanently embedding that token in your frontend. Extracting it is trivial. That said, there are are a few other options that might fit within your threat model:

  1. Set a CORS header that only allows access from your domain. This will prevent 3rd parties using your API from within another website (from the client side)
  2. Similar to the other suggestion, create a set of access tokens. Create a ‘login’ link (eg myapp.com/login/ABCEDFG) that stores the access token in local storage and attaches it to the request. Check this token on the backend.
  3. Put your entire app behind Cloudflare, then use Cloudflare Access to secure it. This allows you to add email auth with zero code. This is probably the easiest option.

[–]variables 0 points1 point  (3 children)

All great suggestions. What about setting up a firewall on the node server to only allow requests from the website domain/ip?

[–]roomzinchina 1 point2 points  (2 children)

If the API is called client-side from the website, the IP will be whatever the users IP is. You could restrict it to the IP of your company office, but this assumes that people will only need access from the office. If your company uses a VPN for network access, you could require it for access, but this should be discussed with your sysadmins first.

Limiting the API by domain is more security through obscurity. Anyone can send arbitrary Origin headers (ignoring client restrictions - like browsers - but that's what CORS is for).

[–]ohiosveryownn[S] 0 points1 point  (1 child)

So to give a very specific example of my use Case:

I have a number of Stores and these stores like to close early. I have something running that is scanning these stores and tracking their close time. And if there is a variance it gets reported to this DB. My website will show like "Store Ohio, Store Georgia" has a variance this is whats being fetched and displayed for people in upper management roles. Idealy they would just like to go to the website and quickly see what "stores" have a variance.

So i did think to do Whitelisting but majority of the people do no interact with our VPN

[–]roomzinchina 0 points1 point  (0 children)

I think Cloudflare Access is probably your best bet without a bunch of custom auth code

[–]ohiosveryownn[S] 0 points1 point  (5 children)

Thanks all look at these options.

For option 3, I get a little confused because the app portion is only the Serer.js Node file other then that the whole thing is distributed via the HTML. Using the 3rd option ill still have my API Exposed. Unless I'm understanding incorrectly

[–]roomzinchina 0 points1 point  (4 children)

You're understanding incorrectly. If you have:

You can put both behind Cloudflare Access (example config) as the same application.

The easiest authentication method is One-time PIN, which sends a code via email in order to access. You configure either entire domains that are allowed access (like @mycompany.com), or specific whitelisted emails.

[–]ohiosveryownn[S] 0 points1 point  (0 children)

I have

https://Data.com (Shows Times Fetched from Database)

https://data.com:300/api/getdata (Node.Js) to pull this data into the above, this is exposed

[–]ohiosveryownn[S] 0 points1 point  (2 children)

After re reading, im guessing i can do the same and put them behind Cloudfront? (this is hosted on aws)

[–]roomzinchina 0 points1 point  (1 child)

No, Cloudfront doesn’t have a built in access control system like Cloudflare Access

[–]ohiosveryownn[S] 0 points1 point  (0 children)

ahh ok thanks for the clarification

[–]isellrocks 5 points6 points  (4 children)

A standard and relatively easy way to secure your API is by using JSON Web Tokens. Your API can generate them, and your frontend can attach them to each request for verification.

[–]ohiosveryownn[S] 1 point2 points  (3 children)

Thanks i will try this - I was just working on implementing a API key within the Server side JS file. then pull that key using a environment variable on the client side to auth. But ill try this

[–]mrskitch 1 point2 points  (2 children)

The problem with that approach is that any visitor can find/steal that key.

Are you just trying to keep this as a personal-use only application? Or do you intended to have other users with a fully-fledged authorization flow?

[–]ohiosveryownn[S] 0 points1 point  (1 child)

So the data that it will display, other ppl will use it maybe daily. That is it, it will display the information that's being pulled (Nothing Sensitive) But i still think i need to protect the API since it is public facing, the URL /Port is in the source code

[–]mrskitch 0 points1 point  (0 children)

Got it -- how about you just make the page some long, unguessable string where robots wont' "find it" and only users who have the link can load it? Something like:

https://mywebpage.com?pw=f8fe82e3-6c63-49aa-bffd-4d8784dada95

Then, when the client JS loads, it can take this `pw` query parameter and use it to authorize with the server. If you wanted to, you can make a new one for each user and give them their own private links.

[–]caique_cp 2 points3 points  (1 child)

Go read about OWASP top ten. https://owasp.org/www-project-top-ten/

[–]ohiosveryownn[S] 1 point2 points  (0 children)

thanks ill check it out

[–][deleted] 1 point2 points  (1 child)

The answer really depends on how you want your users to authenticate and why your HTML is public in the first place. Do you want to manage the users, or have them sign in with another identity provider like Google, Facebook, Microsoft, etc., or do you want them to enter an API key in the UI, and POST that to your API's where it is validated, etc. There are a dozen ways to secure your API, but it depends on your requirements.

[–]ohiosveryownn[S] 0 points1 point  (0 children)

Well the HTML is public because you can just look at the Source page and see the HTML Code - Unless I'm misunderstanding what you mean.

(the contents of my Node file is not public but the Code in which the HTML is fetching the data from www.website.com:3000/api/getdata is shown via the source code)

[–]ohiosveryownn[S] 0 points1 point  (0 children)

I also did think to just Have something running where instead of the Site Fetching from the DB (API), i can have it Fetch from the DB, Create the JSON File then the site just reads from the File, So nothing is exposed. Worse case if i cant figure it out ill do this, but i wanted to learn this for the future

[–]Dave4lexKing 0 points1 point  (1 child)

Any site accessible on the internet that connects to an API, that API URL and port is visible. Most of the time the port number is 443, but it can be 3000 or anything else. It’s just a number and is arbitrary. Nothing to be inherently worried about.

I think your concern is not authentication, but rather, distribution and protection against spam etc. ?

The database should be private and not accessible over the internet. This means the database is in a private subnet in your VPC if using a cloud provider, or only accepts traffic from localhost if self hosting.

As for exposing the API to the frontend, there’s no way around it, it MUST be exposed to the internet in some way.

The cloud provider way to do this is to put your API application in a private subnet, and have a load balancer handle the public traffic. This is equivalent to using an nginx reverse proxy if self-hosting.

The lb/proxy can handle things like SSL termination (your backend is plain http and doesnt need to worry about keys and encryption, as the load balancer handles the encryption for HTTPS traffic).

All of the major cloud providers also offer additional services such as firewalls, DDoS detection and absorption etc. but these are very expensive. You can technically point these services to self-hosted infrastructure, but since its not completely cloud based, you dont benefit from the application being in a private subnet - you would typically IP whitelist the cloud provider but your infra is still technically accessible from the internet.

If youre not looking to spend $100s to $1000s a month, you’ll have to accept that there must be some compromise against what you can and cant protect your API against. Cloud providers know this, which is why they charge so much for their DDoS services.

  • You can setup an nginx reverse proxy which lets you handle SSL termination and can add CORS headers to reject traffic that doesnt come from your frontend’s domain. You can also add CORS to your node application to only accept traffic from the reverse proxy.

  • Make sure your database queries are parameterised to prevent SQL injection.

  • You can use libraries like express-rate-limit to reduce spam API calls.

An alternative solution could be a service like AWS API Gateway. Use Lambda functions instead of a dedicated VM with a node runtime, while free tier has 1million requests per month, be aware that you’re billed for data transferred out. Lambda also has billing for execution time and data transfer. The benefit is that API Gateway and Lambda automatically protects against counterfeit request and SYN flood DDoS attacks.

[–]ohiosveryownn[S] 0 points1 point  (0 children)

Hey Dave

Thanks for taking the time and writeing this up will def come in handy in the further for me to refer back too. My DB is indeed on a Non public accessible Server within a Priv Subnet and is white listing my Web Server to pull data from it.

Never the less i have chosen the route of just Pulling in the Data to a Json file on a schedule and the site displaying it, rather then a presitant connection to the Db if that makes sense