How to restrict API's to run only in the browser? - javascript

I am using node js and i put polices to restrict the api's accurance otherthan in browser.For that i put the following condition
app.route('/students').all(policy.checkHeader).get(courses.list)
exports.checkHeader = function(req, res, next) {
var headers = req.headers;
if ( headers['upgrade-insecure-requests'] || headers['postman-token']) {
res.status(401).json('Page not found');
} else {
return next();
}
}
I am not sure whether my process is correct.I am searching for the common parameter(header-parameter) that exists only for the browser.Can anyone please help me.Thanks.

This is impossible.
You can't control what types of clients make HTTP requests to your HTTP server.
You can't reliably identify what type of client has made a request you receive.
An upgrade-insecure-requests header can be sent (or not sent) with any value by any custom client. Ditto postman-token. Ditto user-agent. Ditto everything else.
The only way to restrict it would be to require some kind of secret in the request. If you want regular web browsers to access it, then the secret will leak through the browser developer tools.

I don't think it's possible to really block the requests without Authentication / Authorization. However, you can use the HTTP Header's User-Agent field
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Related

Restrict PHP API for specific domains which are saved in my database

I have created an API which takes the hostkey or API_KEY and then it validates and gives back JWT token. Everything is working fine, I can't access the restricted routes without Hostkey.
ISSUE
The major issue is that what will happen if someone gives this hostkey to others as it will no longer be protected or it will be misused. So what I want to do is not only validate the hostkey but also validate the domain from which request came from. It is kind of paid service and I really want to restrict is to specific domains. Just like google does with MAP Api as if we add that map key to other domain it throws an error.
The only way to do this is to check the origin and referrer headers.
Unfortunately, server to server this can't be done reliably as the referrer and origin headers would be set by the coder and so can be spoofed easily. For server to server calls you would be better off whitelisting IP addresses that are allowed to make calls to your APIS. In this case use something like How to get Real IP from Visitor? to get the real IP of the server and verify it against whitelisted IPs.
Assuming this is a JS call in browser and not server to server, and that you trust the browser, the only way this can really be done is by verifying the referrer and origin headers. This can still be spoofed with a browser plugin or even with a tool like Postman so I don't recommend it for high security. Here is a PHP example for verifying the origin or referrer.
$origin_url = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_REFERER'];
$allowed_origins = ['example.com', 'gagh.biz']; // replace with query for domains.
$request_host = parse_url($origin_url, PHP_URL_HOST);
$host_domain = implode('.', array_slice(explode('.', $request_host), -2));
if (! in_array($host_domain, $allowed_origins, false)) {
header('HTTP/1.0 403 Forbidden');
die('You are not allowed to access this.');
}
Optionally also CORS headers are good as commented by #ADyson Cross-Origin Request Headers(CORS) with PHP headers
I would like to suggest making a quote or limit for the number of request, so when the paid API reach for 100 request the key will stop working, then the person who paid will not give the key for others. This is not perfect solution, but I would suggest it cause most API services uses it.

How do I limit access to my Netlify Serverless function

I've searched the netlify docs and I can't figure this out.
I have a serverless function located here
/.netlify/functions/orderCreate
But I can hit this in my browser or with curl and it tries to create an order. If an attacker finds out about this function they could create thousands fake orders in my db.
I know I can do some simple checks like make sure it is a HTTP post, or make sure it has some valid session ID but I would really like some type of auth or better security.
Because all requests should come from the a client side react app via an ajax request can I limit it to the same domain or something ?
As Netlify doesn't provide a way to check and specific requests based on origin, you could do it manually from inside your function's code and send a 403 response if the Origin isn't your client-side domain:
exports.handler = function(event, context, callback) {
if (event.headers["Origin"] !== "https://whateverisyourdomainname.netlify.com")
return callback(null, { status: 403 })
// else, do whatever your function does
}
Recent browsers do prevent a user from setting the Origin header himself. However, nothing prevents anyone to craft a curl request and to spoof the Origin header to hit your function. If you wish to really prevent it, you should set-up a proper authentication process to your application.

CORS policy: No 'Access-Control-Allow-Origin' / 500 (Internal Server Error) Problem

Basically, I'm trying to get a username by id from Sequelize. The problem is that I am either stuck with a CORS problem or 500 Internal Server error depending on the response(status)
cors and 500
controller code
async getUserFromUserId (req, res) {
try {
// const user = await User.findByPk(req.body.id)
const id = req.body.id
const user = await User.findByPk(id)
res.send(user.username)
} catch (err) {
// or res.status(some random number).send() for CORS problem to appear
res.status(500).send({
error: 'an error has occured trying to fetch the users id'
})
}
},
client code
this.notifiedUser = (await UserService.getUserFromUserId({id: UserId})).data
I get a Status: 200 OK from postman though.
Postman Solution
Edit:
I have seen how the other Solution for the cors thingy, but the solutions does not specify as to why I get "undefined" results after resolving the cors problem.
So, CORS is actually really obnoxious in this regard, but there's a fairly straightforward way to fix this. It's a super useful security feature, though it is frustrating at best sometimes.
Your browser does what is called a Preflight Request, which is of the http verb OPTIONS. Your browser calls whatever route you want, but instead of what you asked it to do, it calls using OPTIONS first. Your server should accept all routes that the client can ask for with the OPTIONS method, and your server should respond with the following headers to be an externally available, cross-origin API.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, ...
(note, you should not put the ... in, but you can put any HTTP verb in this list)
If you require your own headers (for auth purposes), you want to add this header for Client -> Server.
Access-Control-Allow-Headers: YourHeader, YourHeader2, YourHeader3
You want to add this one for Server -> Client
Access-Control-Expose-Headers: YourHeader,YourHeader3
Note that the OPTIONS call is an entirely separate call that you should handle as well as the GET method.
You've now told the browser what it is allowed to ask for, and what it can expect to get back from your API. If you don't respond to the OPTIONS request, the browser terminates the request, resulting in a CORS error.
I'm going to take a gander at a guess and assume you're likely using Express, which this answer describes how to set the headers on.
What do the headers mean, in English?
Access-Control-Allow-Origin
From where are clients allowed to access this resource (endpoint)? This can match partial domains with wildcards, or just a * to allow anywhere.
Access-Control-Allow-Methods
What HTTP methods are permissible on this route?
Access-Control-Expose-Headers
When I get a response from the server, what should I (the browser) expose to the client-side?
Access-Control-Allow-Headers
What am I as the client side allowed to send as headers?
Okay, so I figured out the problem.
In a way, I did not have to deal with any of the cors stuff because I believe that was not the main source of the problem.
So, instead of accessing my database data through "GET" and getting the data by doing this:
this.data = (Service.function(bodyValue)).data
I did "POST" to get the data, and accessed the data by simply doing this
const response = Service.function({
id: bodyValue
})
this.data = response.data
This accesses the data without having to get "secured" information from the database, but by accessing the data from the database by getting Observer object info from the database.
The Observer object looks as follows, which treats the user data as an object instead of pure data.
Compared to a data object, where each data {...} has user information.
I am not sure if I am using the correct words, but these are to the extent of my current understanding.
If your origin is from localhost, Chrome usually blocks any CORS request originating from this origin.
You can install this extension:
https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=en
Or you can disable the security when running chrome (add the flag):
--disable-web-security

check that requests come the web and not manual

I have a simple autocomplete script in js that takes what the user type, send it to the server and the server checks for data that resembles it. working great.
$(".name_autocomplete").autocomplete({
serviceUrl: '/names',
onSelect: function (suggestion) {
$(this).value = suggestion;
}
});
But since that server endpoint is public, I don't want users to abuse it and have it spit out everything by bruteforce
Is there a way (in clientside or server side) to limit the requests for that url only from the website on that specific page and not from things like curl?
Simplest option would be to drop connections without a user-agent string (similarly with user agent strings that belong to known bots). Of course, if your users are clever enough to use curl, they probably know how to use a fake user agent with it (by default curl won't send a user agent string), so this will only thwart the casual busybody.
For nginx:
if ($http_user_agent ~ (agent1|agent2) ) {
return 444;
}
Maybe HTTP-Access-control can help you in combination with a well set user-agent:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
You can control the origin domain and decide, which domain is allowed to access and which not.

Whitelist PDF Embed

Is there a way or a service that allows for read-only (no saving or downloading) PDF embeds on one domain only? I know that there are services like Scribd and Slideshare but the problem is that while they do have private options, no service as far as I can tell allows to whitelist embeds (eg, only allow embeds on certain domains.) Vimeo can do this with videos and I don't mind paying for this service either. Any ideas?
I've also looked into PDFJS and it seems they have a NodeJS implementation so I was thinking maybe PDFJS could grab the PDF from the server on the server side and just stream it to the client without exposing the original PDF url. However I couldn't find good documentation for PDFJS.
Any help would be greatly appreciated.
This can be achieved with any HTTP server, but since you mentioned Node in your question, we will solve the problem with that technology. I am assuming Express Framework as well.
First you simply host the PDF as a static file on your server. Then you would register some Middleware that detects a request for the PDF. If the hostname that is requesting does not match a list of "approved" domains, then you serve an error back to the client. If the domain is approved, you serve the PDF. This is no different then a .htaccess file in Apache that limits access by domain/IP or an "allow" block in a Nginx config. Here is a quick look at the Middleware function...
var approved = []; // Add your approved domains here.
// Make sure this middleware comes before app.use(express.static)
app.use(function(req, res, next){
if(req.url == '/path/to/PDF') {
if(approved.indexOf(req.headers.host) {
next();
} else {
next(new Error('Nu uh uh!'));
}
} else {
next();
}
});
This way even if they copy the embed code, they will get an error from the server (probably should be a 403, but those are semantics you can decide on yourself)

Categories

Resources