Public API accessible only from website - node.js

When a user land on the home page, the website does an ajax call to api/posts to retrieve a list of posts.
What I want to do is to make that url accessible only from the site, that
means even a cul http://locahost:3000/api/posts should not work.
I've looked for tons of articles and seems that the best way to do it is to pass a secret token on the requests headers + HTTPS, but the issue is there, that token will be stored on the client side so some guys that know a bit about security could eventually find it.
Ideally I would like to do the checking on server side only without passing anything from the client.
I'm using express

Related

What is the safest method to make session?

So I have few things to say I don't want to use cookies so things like express-session doesn't come as option.
I use nodejs with express with no front-end JavaScript and mysql as database. I don't really know how to do it so I would like to hear your opinion.
I already tried to search on internet.
When dealing with regular web pages, there are only four places in a request to store information that would identify a session.
Cookie sent with each request
Custom header on each request
Query parameter with each request
In the path of the URL
You've ruled out the cookie.
The custom header could work for programmatic requests and is regularly used by Javascript code with various types of tokens. But, if you need a web browser to maintain or send the session on its own, then custom headers are out too.
That leaves query parameters or in the path of the URL. These both have the same issues. You would create a sessionID and then attach something like ?sessionID=92347987 to every single request that your web page makes to your server. There are some server-side frameworks that do sessions this way (most have been retired in favor of cookies). This has all sorts of issues (which is why it isn't used very often any more). Here are some of the downsides:
You have to dynamically generate every single link in a web page so that it will include the right sessionID as part of the link so if the user clicks on it, the resulting http request will have the right sessionID included.
All browser caching has to be disabled or bypassed because you don't want the browser to use cached web pages that might contain the wrong sessionID.
User bookmarks basically don't work because they end up bookmarking a URL with a sessionID in it that won't last forever.
The user sees sessionID=xxxx in all their URLs.
Network infrastructure that log the URLs of requests will include the sessionID (because it's in the URL). This is considered a security risk.
All that said and with those tradeoffs, it can be made to work, but it is not considered the "safest" way to do it.

Restrict api access in Node JS express

I have an express server with a few API routes like this:
server.post("/api/send-email", (req, res) => {
});
});
You don't need an auth token to access the API, but I only want my website mydomain.com to be able to use it.
I have tried enabling restricting access like this:
function restrictAccess(req, res, next) {
if (req.headers['origin'] !== 'http://localhost:3000') {
res.sendStatus(403);
} else {
next();
}
}
And I then passed restrict access into my route as a middleware.
When I make a POST request with postman I can't the API anymore, but I can just change the origin header and am able to access it again.
How can I allow only requests from mydomain.com? I have searched the internet for a long time now, but couldn't find anything. Is it even possible?
How can I allow only requests from my own webpages at mydomain.com?
In a nutshell, you can't. Any tool like postman or any script (such as node.js, PHP, Perl, etc...) can send whatever headers or other request parameters it wants so headers by themselves are useless for restricting access to only a web page in your domain.
That's just how the web works.
Restricting access would more commonly require a user login or some credential like that (probably setting a cookie that you can check) and then if you see that your APIs are being abused, you can ban/remove a specific user account that is doing it.
There are other techniques that may make it more work for scripts or tools to use your API, but even they are not immune to a hacker that wants to put in the work. For example, your server can generate a token, embed it in your web page and then whenever you make an API request from your web page, you include the token from the web page. Your server, then checks for the presence of a valid token. You make sure that tokens expire in some reasonable amount of time so a hacker can't just get one and use it for a long time.
A determined hacker can still scrape a token out of your web page whenever they want to use it so this is only an obstacle, not something that stops a determined hacker.
The only real solution here and what someone like Google uses for their APIs is to require some sort of credential with each API call and then instrument your server for abuse of the APIs (rate limiting, unintended use, etc...) and then revoke that credential if it is being misused. The credential can be a developer token (as with some Google APIs) or it can be some sort of authentication credential from a user login (like perhaps a cookie).
There are other tricks I've seen used before where an API only works properly if a sequence of requests came before it that would normally be coming from your web page. This is a lot more work to implement and maintain, but if your web page would normally issue a request for the web page, then make two ajax calls, then request five images and then call the API, you can somehow have your server track this sequence of events from a specific browser and only if you see the expected sequence of events that looks like it's coming from a real browser web page, so you allow the API call to work. Again, this is a lot of work and still not infallible because determined hacker can just use something like puppeteer to automate an actual browser.
Major browsers send along the origin header without permitting any browser Javascript to modify it.
Non-browser API clients, like Postman and anything else, can set the origin header, and other headers, to whatever they choose. Non-browser API clients can easily spoof your API pretending to be browsers.
Therefore, security tip, using the origin header's value to decide whether to grant access to your API offers you no security whatsoever.
You really do need some kind of token access mechanism. Especially for an API that sends email. If a cybercreep finds your API, your hosting service will accuse you of sending spam.
Sorry about that. :-( Security is a pain in the neck.

How to make Node API only accessible by web app?

I'm developing a web app with React and an GraphQL API with Node.js / Express. I would like to make the API more secure so that its harder for API requests that don't come from the web app on the browser to get data. I know how to do it with registered users. But how to make the non-registered user still be able to access some basic data needed for the app?
Is it possible to put some kind of key in the web app - so the API call can't be replicated for others through sniffing the network dev tool in browser and replicating in Postman? Does SSL/TLS also secure requests in that browser tool? Or use like a "standard" user for non-registered visitors?
Its a serverside web app with next.js
I know theres no 100% secure api but maybe its possible to make it harder for unauthorized access.
Edit:
I'm not sure if this is a problem about CSRF because Its not about accessing user data or changing data through malicious websites etc. But its about other people trying to use the website data (all GET requests to API) and can easily build there own web app on top of my api. So no one can easily query my api through simple Postman requests.
The quick answer is no you can't.
If you trying to prevent what can be describe as legit users form accessing your api you can't really do it. they can always fake the same logic and hit your webpage first before abusing the api. if this is what your trying to prevent your best bet is to add rate limiting to the api to prevent a single user from making too many request to your api (I'm the author of ralphi and
express-rate-limit is very popular).
But if you are actually trying to prevent another site form leaching of you and serving content to their users it is actually easier to solve.
Most browsers send Referrer header with the request you can check this header and see that requests are actually coming from users on your own site (this technique is called Leech Protection).
Leaching site can try and proxy request to your api but since they all going to come from the same IP they will hit your rate limiting and he can only serve a few users before being blocked.
One thing the Leecher site can do is try to cache your api so he wont have to make so many requests. if this is a possible case you are back to square one and you might need to manually block his IP once you notice such abuse. I would also check if it's legal cause he might be breaking the law.
Another option similar to Referrer is to use samesite cookies. they will only sent if the request is coming directly from your site. they are probably more reliable than the Referrer but not all browsers actually respect them.

How do I tell the client they gave the wrong password?

I have a node.js web app, and I'm working on getting the user logged in and authenticated. I'm using pug to template things, mysql, and express.js.
I use bcrypt for hashing the password, and that's all working right. My problem lies with what to do when the password is incorrect, and how to tell that to the client, as the checking happens on the server.
Initially I was using socket.io, but I moved away from that. Then, I used a redirect to /login?error=true. Now that I'm using pug templating, it can't find the view. One solution would be to simply redirect to a whole new page, /loginfailure, but I feel like I should be able to accomplish this without redirects.
I see res.json(), but when I use that, the client side renders it and I end up with a blank page that's just the json that I sent. Is there a way to send json so I can do something with the json data client side, rather than have it just render the data?
What's the best way to send data from the server to the client, preferably not as a redirect?
I'm looking into AJAX now, and that seems like it has potential. The issue I'm having is that if I use res.send or res.json it still renders the data, rather than passing it to the callback of the ajax call. Suggestions?
Typically in express, and in whatever file you have your routes, you can implement an auth service to make sure that they are logged in to even see the rest of your api. Usually I'll give tokens to users, once they've been authenticated then I would let them see the rest. You can also use a service like ui-router and create states for your app, and if they don't have correct login credentials typically they would stay at the home page which would be the redirect. You could also alert them with a modal or something saying invalid email/password.

What stops someone from reading CSRF tokens in form inputs with JS

Most frameworks I've looked at will insert into forms a hidden input element with the value being a CSRF token. This is designed to prevent user Bob from logging in on my site and then going to http://badsite.com which embeds img tags or JS that tell my site to execute requests using Bob's still logged in session.
What I'm not getting is what stops JS on badsite.com from AJAX requesting a URL with a form on my site, regex-ing the CSRF token from the hidden input element, and then AJAX posting to my site with that valid CSRF token?
It seems to me that you'd want to use JS to insert the CSRF token into the form at runtime, pulling the value from a cookie (which is inaccessible to badsite.com). However, I've not heard this approach mentioned and so many frameworks do the simple hidden input with the CSRF token, I'm wondering if my solution is over-engineered and I'm missing some part of what makes the hidden input method secure.
Can anyone provide some clarity? Thanks!
what stops JS on badsite.com from AJAX requesting a URL with a form on my site
The Same Origin Policy (unless you subvert it with overly liberal CORS headers). JavaScript running on a site can't read data from a site hosted on a different host without permission from that host.
There are workarounds to the SOP, but they all either require the co-operation of the host the data is being read from (JSON-P, CORS), or don't pass any data that identifies a specific user (so can't access anything that requires authorisation).

Resources