Spoof Origin header in Node.js - node.js

I have a node.js server running express. I'm trying to write several tests for it in order to confirm standard behavior in the future when someone else may work on it. The server processes the request's header, the referrer and origin fields specifically. Using Jquery's ajax method, I was able to send ajax requests to the server. I found that I was able to set the referrer field. However, I can't seem to set the origin. Is there any way to spoof the origin in node.js?
Edit:
For clarification, I'm not running this code through a browser, but from the command line. Normally the ajax call would be run from a webpage, but as I'm writing tests to be run from Mocha, the origin isn't set.

Due to security reasons, the browser will not allow you to manually set your request origins. To spoof your request origin, you will have to make the request server-side:
var http = require('http');
var opt = {
host: 'yoursite.com',
path: '/test',
headers: { origin: 'http://spoofedorigin.com'}
};
http.get( opt );

Related

HTTP request working from Postman and Node but not React

There are a few questions similar to this on Stack Overflow, and none of the proposed solutions worked, so I'll walk through the case and what I've tried.
I have a server application hosted on Cloud Run, which can only be accessed with the appropriate Bearer token in the request Authorization header. I've tried accessing it via Postman and an Axios request from a local Nodejs server, with the Authorization header, and it worked fine. With React (create-react-app specifically), I get the following error: Access to XMLHttpRequest at 'https://myserver-lhp5a9xp5a-ue.a.run.app/api/rules' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
On the server side, I get the 403 error that Cloud Run gives when the incorrect Authorization token is passed. Also, when I allow unauthenticated access from the Cloud Run side (so remove the need for an Authorization header), the request works fine, so it looks like this is indeed an issue with the Authorization header and not CORS.
In addition, I'm handling CORS on the server side. Here's my server-side code:
var express = require('express');
var router = express.Router();
const cors = require('cors');
router.options('/api/rules', cors());
router.get('/api/rules', cors(), (req, res, next) => {
res.status(200).send()
});
Here's my React code:
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_BASE_URL
});
const buttonClickHandler = async (event) => {
const resp = await axiosInstance.get('/api/rules'
, {
headers: {
'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsImtpZ...' // I used this token within the same minute when trying the request via Postman or from my Nodejs app, so a token expiry isn't the issue.
}
}
)
console.log(resp.data)
}
Here's what I tried so far:
Using fetch instead of axios - same error
Using the same token, within the same 5 seconds, to send the request from Postman or a Nodejs server - it worked fine.
Using an axios interceptor to set the Authorization - same error
Removing the single quotes around Authorization - same error
Sending the request to my Nodejs server instead and doing a console.log of the header to make sure the Authorization token is being passed correctly (it is)
Not using an an axios instance but spelling out the full URL in the request - same error
Trying a different endpoint on my Cloud Run server - same error
Deploying my React app to be served from a https endpoint and sending the request from there - same error
Adding Accept: '*/*' to the headers
Adding 'Accept': '*/*' to the headers
Adding 'Content-Type': 'application/json' to the headers
All combinations of the three above points
I found the answer after some digging, thanks #aniket-kolekar for pointing me in the right direction.
When Postman or a Nodejs server query an endpoint like GET, POST, PUT, DELETE, they send the call without checking the OPTIONS first. Create-React-App does.
The service I was querying is hosted on Cloud Run and doesn't allow unauthenticated invocations. So while I was including the authorization header to make my GET call, it wasn't being included in the pre-flight OPTIONS call. In fact, CORS prevents auth headers from being included in an OPTIONS call.
A Cloud Run PM replied in this post that this is a known issue with Cloud Run. The way I'll get around it for now is to host two services on Cloud Run - one that doesn't require authentication, and effectively acts as a proxy server to route calls from the client service to the shielded server service.
TLDR;
CORS is a mechanism built into the web browser. It’s not a UI code issue.
To fix CORS problems, you need to make changes on the API (server) side.
Here is the behind the scenes working:
Browser: Sends OPTIONS call to check the server type and getting the headers before sending any new request to the API endpoint. Where it checks for Access-Control-Allow-Origin. Taking this into account Access-Control-Allow-Origin header just specifies which all CROSS ORIGINS are allowed, although by default browser will only allow the same origin.
Postman: Sends direct GET, POST, PUT, DELETE etc. request without checking what type of server is and getting the header Access-Control-Allow-Origin by using OPTIONS call to the server.
You will have to configure Access-Control-Allow-Origin header in your server to resolve the CORS issue.

Postman Requests Receive a HTTP 401 Status Code

I am working on creating a Node.js REST API, using the Express module, that redirects HTTP GET and PUT requests to another server. However, when running test queries in Postman, I always get HTTP 401 Unauthorized responses. Yet, when I try the same on query on the Chrome browser I get a successful response (HTTP 302). I read through some documentation on the HTTP request/response cycle and authorization. The server I am redirecting to uses HTTP Basic authentication. In my code I am redirecting the API call to my application server using the res.redirect(server) method. In my Postman request I am setting the username/password in Authorization tab for my request. I know this is gets encoded using base64, but I am guessing this isn't being passed on the redirect when done through Postman.
The following code snippets show what I've created thus far.
This is the Express route I created for GET requests
app.get('/companyrecords/:name', function(req, res) {
var credentials = Buffer.from("username:password").toString('base64');
console.log(req);
var requestURL = helperFunctions.createURL(req);
res.redirect(requestURL);
});
I define a function called createURL inside a file called helperFunctions. The purpose of this function is set up the URL to which requests will be directed to. Here is the code for that function.
module.exports.createURL = function (requestURL) {
var pathname = requestURL._parsedUrl.pathname;
var tablename = pathname.split("/")[1];
var filter = `?&filter=name=\'${requestURL.params.hostname}\'`;
var fullPath = BASE_URL + tablename.concat('/') + filter;
console.log(fullPath);
return fullPath;
}
Where BASE_URL is a constant defined in the following form:
http://hostname:port/path/to/resource/
Is this something I need to change in my code to support redirects through Postman or is there a setting in Postman that I need to change so that my queries can execute successfully.
Unfortunately you can't tell Postman not to do what was arguably the correct thing.
Effectively clients should be removing authorisation headers on a redirect. This is to prevent a man-in-the-middle from sticking a 302 in and collecting all your usernames and passwords on their own server. However, as you've noticed, a lot of clients do not behave perfectly (and have since maintained this behaviour for legacy reasons).
As discussed here however you do have some options:
Allow a secondary way of authorising using a query string: res.redirect(302, 'http://appServer:5001/?auth=auth') however this is not great because query strings are often logged without redacting
Act as a proxy and pipe the authenticated request yourself: http.request(authedRequest).on('response', (response) => response.pipe(res))
Respond with a 200 and the link for your client to then follow.

How does CORS work in AWS, NodeJs and other web services? [duplicate]

This question already has answers here:
How does the 'Access-Control-Allow-Origin' header work?
(19 answers)
Why doesn't adding CORS headers to an OPTIONS route allow browsers to access my API?
(36 answers)
Closed 4 years ago.
I was working on a AWS project, and noticed that in order to allow CORS, my seniors had set headers property Access-Control-Allow-Origin, in response. So whole response was like:
module. exports.handlerFunction = async ( event, content) => {
// code here for DB calls and other logic
return {
headers: {
"Access-Content-Allow-Origin" : "*"
},
statusCode: 200,
body: result
};
}
And my thoughts were, how is it even working and allowing CORS?. What if we didn't wanted origin to perform any operations and had done "Access-Content-Allow-Origin": "https://example.com".
Since we are setting this in response, so the origin which was not supposed to do anything and just return, now have done everything and then responded with error of CORS. I asked my seniors, "How this is working and How CORS work?", the response was its browser property and browser send a pre-flight request and check for CORS. But we check for CORS at end, once every thing is done, how did pre-flight request skipped all our checks DB and API call and just landed at end and check for response headers. They had no answer and said same pre-flight concept. I asked next question "browser have pre-flight concept to check for CORS, what about postman, cUrl requests and then API call via various programs like node-fetch, request, https API call module in NodeJs do they also make pre flight call".
Also when I was creating my NodeJs express Server application, I used cors.js a NPM module. With that I checked for CORS before entering into any API function, on entry of every call and only allowed permitted source to enter. Code is like:
const CORS = require('cors'),
express = require('express');
const app = express();
let allowedOrigin = ['https://example.com'];
let corsOps = {
origin: (origin, cb) => {
if (allowedOrigin.includes(origin))
cb(null, true);
else
cb(new Error('Not allowed'));
}
};
app.use(CORS(corsOps));
This checked before calling any function and not on response.
I searched a lot about this behavior and have seen multiple examples of using CORS in headers, How does it even work in headers?.
For me it's my backend that stops call and check who is calling backend API.
How can someone who is making requests set property in headers and backend open its access to anyone, just by seeing headers property and not checking source that called?
When browsers execute an AJAX call, they first check if the target origin matches the current origin a.k.a. window.location.origin. If not, they check if the target origin accept window.location.origin as source of a CORS request. If not, the browser shows the infamous No 'Access-Control-Allow-Origin' header is present on the requested resource. error message.
If the request can be made, the browser includes the Origin header in the HTTP request with the value of window.location.origin.
There are multiple ways to set up the CORS policy in the backend:
Accept all origins with the wildcard
Only accept a preconfigured set of origins
Dynamically take the value of the Origin header as the value of the "Access-Content-Allow-Origin on a request by request basis
In the last case, the origin may be obviously spoofed like so:
curl -H"Origin: https://www.example.com" https://api.example.com/some/path
Whether or not a server should serve a request should not obviously depend on the Origin header but on other factors such as an Authorization header or an appropriate authorization Cookie; validity of request parameters etc.

Cross-Origin HTTP Request originating from server-side NodeJS/Axios/JSDOM

I am using Axios to create a HTTP request to a API server in different domain.
The API server allows Cross-Origin requests from http://localhost:3000.
I have no control over the API server.
My app usually runs in http://localhost:3000 and makes requests from browser.
There's no problem up to this point. Cross-Origin requests are working fine. However, recently I want to add a unit test for those API calls. This test environment is jsdom since I'm using Jest. This raises a problem when I create HTTP request from server-side, the origin is set to http://localhost, which the server does not allow.
The request is made using Axios:
axios.post(`${API_DOMAIN}/member/account/login`, {
username,
password,
}, {
headers: {
Origin: 'http://localhost:3000'
}
})
However, the response still says that
error: Cross origin http://localhost forbidden
How to change the "Origin" of the HTTP request I create with Axios under jsdom to other than http://localhost? I need it to be http://localhost:3000 so that the API server allows me.
It turns out jsdom is the one who makes the origin localhost, and prevented cross-origin requests. From https://github.com/axios/axios/issues/1180 I was able to solve my problem. In the test suite, place this code before any HTTP requests by axios:
axios.defaults.adapter = require('axios/lib/adapters/http')
This will make Axios use NodeJS's HTTP adapter instead of JSDOM's XMLHttpRequests. This way there will be no Cross-origin problem.
Supplimenting #Arkross answer,
axios/lib/adapters/http is not exposed by the package.
Instead, you can do this:
axios.defaults.adapter = 'http'
// or
axios.request({
adapter: 'http',
...
})

CORS-enabled server not denying requests

I am trying to use express Cors with my resitfy server and it doesn't seem to be denying requests coming from other ips. I am working locally so I tried setting origin to a random public ip but all of my requests are still going through
Here is my route:
module.exports = function(app) {
var user = require('./controllers/userController');
var cors = require('cors');
var corsOptions = require('./cors.json');
app.post('/auth/signup', cors(corsOptions),user.createUser);
app.post('/auth/login', cors(corsOptions), user.validateUser);
app.post('/auth/generateKeys', cors(corsOptions), user.generateKeys);
app.post('/auth/generateToken', user.generateToken);
};
and here is my cors.json file where I have set a random ip:
{
"origin": "http://172.16.12.123",
"optionsSuccessStatus": 200,
}
With cors set on the route I can see the following in postman but the request is still going through? I would expect an access denied response.
Access-Control-Allow-Origin →http://172.16.12.123
CORS configuration on its own isn’t going to cause a server to deny requests. You can’t cause server-side blocking of requests just through CORS configuration.
The only thing servers do differently when you configure CORS support is just to send the Access-Control-Allow-Origin response header and other CORS response headers. That’s it.
Actual enforcement of cross-origin restrictions is done only by browsers, not by servers.
So no matter what server-side CORS configuration you make to a server, the server still goes on accepting requests from all clients and origins it would otherwise; in other words, all clients from all origins still keep on getting responses from the server just as they would otherwise.
But browsers will only expose responses from cross-origin requests to frontend JavaScript code running at a particular origin if the server the request was sent to opts-in to permitting the request by responding with an Access-Control-Allow-Origin header that allows that origin.
That’s the only thing you can do using CORS config. You can’t make a server only accept and respond to requests from particular origins just by doing any server-side CORS configuration. To do that, you need to use something other than just CORS configuration.
CORS does not prevent anyone from sending GET or POST requests to your application or exposed API URL.
Instead, it indicates to the web browser that AJAX requests are allowed to this server, from the domain they are executed.
But only AJAX requests executed from a domain are CORS-controlled. Entering the URL in the web browser will not activate CORS: it is not a firewall.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
The order of event is:
Domain A executes AJAX on User's browser to request API URL on Domain B
User's browser sends a basic primary request to target Domain B and checks if CORS are allowed for Domain A
If allowed, AJAX request is executed otherwise null is returned

Resources