How to ensure HTTP trigger will be called only via HTTPS? - node.js

I'm building an API on Cloud Functions with NodeJS and Express (Firebase) and I'd like to accept only calls via HTTPs so calls made over plain HTTP will fail.
Is it possible to do it?

You should examine the request object being passed to your function. It's going to be an Express type Request object. Request has a property called protocol that should be "https". So:
functions.https.onRequest((req, res) => {
if (req.protocol !== "https") {
// reject the request
res.sendStatus(403)
}
})

Generaly you want a application running for each protocol listening to a different port. To solve your problem you could simply ignore the requests for the http instance or redirect every http request to a https request.
Here's a middleware for that:
app.use(function(request, response){
if(!request.secure){
response.redirect("https://" + request.headers.host + request.url);
}
});
See the express documentation.

Related

Do I need to allow only https in my backend server ? [Nodejs]

I have nodejs backend server which runs in port 5000. Currently it supports both http and https requests.
For me its not a problem because my client applications (frontend applications) uses https requests always. But someone else can send http request using (postman) like apps any time.
But I want to know is this secure ? or do I need to convert each http request to https ? If yes how ?
Edit 1 ->
If I need to redirect I can use below code inside server.js,
if (process.env.NODE_ENV === 'production') {
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https')
res.redirect(`https://${req.header('host')}${req.url}`)
else next()
})
}
But if I use this hacker can send x-forwarded-proto as https but the url as http , in that case also response will be http.
So what would be the best way to redirect http to https ?

How to emulate traffic in express.js

I have a node express server responding to http traffic:
const http = require("http");
const express = require("express");
const app = express();
const server = http.createServer(app);
app.use(function(req,res,next){
console.log(`logging: req: ${util.inspect(req)}`);
next();
});
and all that works fine. I'd like to have a program on my node server inject emulated http traffic into the express stack, without a network connection. I can't just magic up a (req,res) pair and call a middleware function like the one in app.use above, because I don't have a next to give it, and my req and res will not be the ones next passes on to the next middleware in the stack.
Edit: What I actually have is a websocket connection sending data packets of a different format, different data contents from http traffic that can also carry the same information. I can take those websocket packets and build from those a request that is in the same format that the http traffic uses. I would like to pass that transformed request through the express http middleware stack and have it processed in the same way. Going all the way back to create an http request having just dealt with a ws request seems a bit far.
What's the simplest way to emulate some traffic, please? Can I call a function on app? Call some express middleware, or write a middleware of my own to inject traffic? Call a function on server?
Thanks!
Emulation traffic by calling some Express.js internal functions isn't the right way. Much easier is to trigger the server by HTTP request from the same process
const http = require('http');
const util = require('util');
const express = require('express');
const app = express();
const server = http.createServer(app);
app.use(function(req, res, next) {
console.log(`logging: req: ${util.inspect(req)}`);
next();
});
const port = 8081;
server.listen(port);
http.request({ port }).end();
From your question
I'd like to have a program on my node server inject emulated http traffic into the express stack, without a network connection
Can you clarify, why without a network connection?
A few things:
You need to make an endpoint
You need to host your server somewhere
You need something to send requests to your server
Express provides you a way to receive requests (req, res) (might be from a browser, might not be), perform some operations, and return responses (req, res) to the requester.
The expression
app.use(function(req,res,next){
console.log(`logging: req: ${util.inspect(req)}`);
next();
});
is actually a middleware function. This will take every request to your server and change the request object created by express into a string, and print it in your server log.
If you want a testable endpoint, you would add this to the bottom of the snippet you posted
app.get('/test', function (req, res) {
res.json({success:true})
})
This tells your app to allow GET requests at the endpoint /test
Next you're going to need to host your express server somewhere you can send requests to it. Your local machine (localhost) is a good place to do that. That way, you don't need an internet connection.
Pick a port you want to host the server on, and then it will be reachable at http://localhost:<Your Port>.
Something like this will host a server on http://localhost:3000. Add this below the route we declared above:
server.listen(3000, function() {
console.log('Server running on port 3000');
});
Finally, you'll need a way to send requests to the server on localhost. Postman is a great tool for testing express routes.
I would suggest installing Postman and using that to emulate http traffic.
Once your server is running, open postman and send a GET request to your server by entering the server address and port, and hitting the blue send button (You'll be sending the request to http://localhost:3000/test).
Here's an image of what postman should look like if all goes well
You should also see your middleware fire and print out the request object in your terminal.
Good Luck!

React - Network Error when sending POST request only when on HTTPS

I have a web application where the front-end is done with React (create-react-app) and it is deployed on Heroku. The back-end is done with node.js/express and it runs on an Amazon EC2.
I don't have any problem getting the app to work when I deploy the front-end on localhost or on Heroku if I access it with HTTP as http://myapp.heroku.com. The problem arises when I access it with HTTPS (which is the default on Heroku) as https://myapp.heroku.com. When I do so and send a request to the node.js server on the Amazon EC2, I get the following error:
Error: Network Error
Stack trace:
createError#https://myapp.herokuapp.com/static/js/bundle.js:1555:15
handleError#https://myapp.herokuapp.com/static/js/bundle.js:1091:14
Here is the part in the front-end which send the request to the node.js server:
_deflateAscii(valueAscii){
axios.post('//My-EC2-Instance-Here.compute.amazonaws.com:80/deflate/ascii', {inflatedAscii: valueAscii}).then(res => {
this.setState(
{
inflatedAscii: valueAscii,
inflatedHex: res.data.inflatedHex,
deflatedBase64: res.data.deflatedBase64,
deflatedHex: res.data.deflatedHex
},
this._setTextBoxesValues
);
}).catch((err) => {
console.log(err)});
}
Here are the modules I use on the server-side:
const express = require('express');
const cors = require('cors');
const http = require('http');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(cors());
And the part handling coming request from the front-end:
app.post('/deflate/ascii', function(req, res){
try{
console.log('request received');
var inflatedHexValue = convert.asciiToHex(req.body.inflatedAscii);
var deflatedBase64Value = deflate.asciiToBase64(req.body.inflatedAscii);
var deflatedHexValue = deflate.asciiToHex(req.body.inflatedAscii);
}catch(err){
console.log(err);
res.status(500).send(err);
}
response = {
deflatedBase64: deflatedBase64Value,
deflatedHex: deflatedHexValue,
inflatedHex: inflatedHexValue
};
res.end(JSON.stringify(response));
console.log('response sent');
});
When I get the Network Error on the front-end, the node.js server does not receive the request, but I have done some diagnostics with Wireshark and there is and handshake with the EC2 server, but the traffic ends there without any HTTP traffic:
TCP-traffic between https://myapp.heroku.com/ and My-EC2-Instance-Here.compute.amazonaws.com
Do I need SSL on the backend if the frontend is HTTPS? From this post I understood that it can also do without Do we need ssl certificate for both front end and backend?. This is just a course project anyway and there is only meaningless strings going back and forth.
When you visit your site over https in a browser, you need to disable blocking of mixed content (http and https). As a result your browser classifies the connection as not secure.
So yes it would be good to use SSL/TSL both for backend and frontend.
Alos did you try to use
res.json(response);
res.status(200).end();
or set the headers to application/json?
I recommend to put your res calls into the try catch block, because your res.end will be executed anyway.

http.request vs http.createServer

What is the difference between the request in this line of code:
http.createServer(function(request,response){. . .}
and request in
http.request()
Are both requests done to the server?
I am new to node.js and I am sorry if I sound dumb!
How does http.request() work?
In http.request() we fetch data from another site but in order to fetch data from another site we first need to go to our site and then make a request? Explain it with a simple real-life example!
http.request() makes a request to another HTTP server. Suppose for some reason I wanted to go download Stack Overflow's home page...
http.request('https://stackoverflow.com/', (res) => {
// ...
});
http.createServer()... it creates an HTTP server. That is, it binds your application to a socket to listen on. When a new connection is made from somewhere or something else, it handles the underlying HTTP protocol of that request and asks your application to deal with it by way of a callback. From the Node.js documentation:
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('okay');
});
These two methods have absolutely nothing to do with each other. http.request() is for making a request to an HTTP server. http.createServer() is for creating your own HTTP server. Don't get confused by the callbacks.
Based on the source code of nodejs (extract below), createServer is just a helper method to instantiate a Server.
Extract from line 1674 of http.js.
exports.Server = Server;
exports.createServer = function(requestListener) {
return new Server(requestListener);
};
The http.request() API is for when you want your server code to act as a client and request content from another site and has GET, POST, PUT, DELETE methods.

nodejs configuration httpd.conf

I'm used to apache and putting configuration items in httpd.conf
Where do these types of configurations go in a node environment. For example, I want to make sure that only GET, POST, and PUT are accepted and Head and Trace are not accepted. Where does a config like that go?
Additional things like Cache-Control and limiting request and response sizes.
Node.js is just a JS framework with a system API. Technically, you could reimplement Apache HTTP Server in Node.js, mimicking its behaviour and its configuration structure. But would you?
I believe you are using Node.js' HTTP module. Look at the docs: there's no way to read configuration from a file. The server is programmatically created using http.createServer. You provide a callback that listens to requests. This callback provides an http.IncomingMessage parameter (first parameter) which contains everything you need.
Here's an example:
// load the module
var http = require('http');
// create the HTTP server
var server = http.createServer(function(request, response) {
// use the "request" object to know everything about the request
console.log('got request!');
// method ('GET', 'POST', 'PUT, etc.):
console.log('HTTP method: ' + request.method);
// URL:
console.log('HTTP URL: ' + request.url);
// headers:
console.log('HTTP headers follow:');
console.log(request.headers);
// client address:
console.log('client address: ' + request.socket.address().address);
});
// listen on port 8000
server.listen(8000);
If you really want a configuration file, you will have to forge it yourself. I suggest creating a JSON configuration file as this can be turned directly into a JS object using JSON.parse(). Then just use your configuration object programmatically to achieve what you want.

Resources