Node.JS, Express and Heroku - how to handle HTTP and HTTPS? - node.js

I have an app which is quite normal Express app - simple server logic, views, lots of client-side JS.
I have to do many AJAX requests. Some of them need to be secured by HTTPS protocol (some needn't).
So, my server should work with both HTTP and HTTPS.
It should also work o both the local machine (ran with nodemon normally) and on Heroku.
As far as I understood, Heroku gives you a single port (process.env.PORT) you can listen to, and handles all requests through the proxy (so, you app is listening to this port and not bothering about the proto - right?)
So, am I getting this right - I should have some different code for dev machine and Heroku?
Like
...
app = express()
...
if process.env.NODE_ENV == 'production'
app.listen(process.env.PORT)
else
https = require('https')
http = require('http')
http.createServer(app).listen(5080) # some local port
options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem') # my self-signed files
}
https.createServer(options, app).listen(5443) # some different local port
Is it the proper way to deal with this?

For the Coffeescript-challenges, here is a version of Guard's answer converted to Javascript. I took a different approach to splitting up the if else statements.
var express = require('express');
var http = require('http');
var https = require('https');
var fs = require('fs');
var privateKey = fs.readFileSync('./config/localhost.key').toString();
var certificate = fs.readFileSync('./config/localhost.crt').toString();
var options = {
key : privateKey
, cert : certificate
}
var app = express();
// Start server.
var port = process.env.PORT || 3000; // Used by Heroku and http on localhost
process.env['PORT'] = process.env.PORT || 4000; // Used by https on localhost
http.createServer(app).listen(port, function () {
console.log("Express server listening on port %d in %s mode", this.address().port, app.settings.env);
});
// Run separate https server if on localhost
if (process.env.NODE_ENV != 'production') {
https.createServer(options, app).listen(process.env.PORT, function () {
console.log("Express server listening with https on port %d in %s mode", this.address().port, app.settings.env);
});
};
if (process.env.NODE_ENV == 'production') {
app.use(function (req, res, next) {
res.setHeader('Strict-Transport-Security', 'max-age=8640000; includeSubDomains');
if (req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'] === "http") {
return res.redirect(301, 'https://' + req.host + req.url);
} else {
return next();
}
});
} else {
app.use(function (req, res, next) {
res.setHeader('Strict-Transport-Security', 'max-age=8640000; includeSubDomains');
if (!req.secure) {
return res.redirect(301, 'https://' + req.host + ":" + process.env.PORT + req.url);
} else {
return next();
}
});
};

Well, community looks quite dead these days (hope I'm wrong)
The answer is:
a) yes, this is the way to deal with it
b) the way to check if you are in secure mode or not depends on the environment as well:
if process.env.NODE_ENV == 'production'
is_secure = (req) ->
req.headers['x-forwarded-proto'] == 'https'
else
is_secure = (req) -> req.secure
ADD
If you wish to force HTTPS:
redirect_to_https = (req, res, next) ->
if not is_secure(req)
res.redirect config.SECURE_DOMAIN + req.url
else
next()
app
.use(redirect_to_https)

You can use app.enable('trust proxy'), then req.secure boolean (http/https) works also on Heroku, or behind any compatible SSL Termination proxy.

Related

I need to secure my node js website to HTTPS running on the default 80 port of HTTP

I am running my server on ionos hosting and executing nodejs on the default port of 80.
I don't know how to enable the HTTPS for it.
Following is my sample node js server creation code:
const Https = require('https');
const fs = require('fs');
const httpsServer = Https.createServer({
key: fs.readFileSync("private.key"),
cert: fs.readFileSync("Dev-2020-09-12-013930.cer")
}, app);
var io = require('socket.io').listen(Https);
global.SOCKET = io;
const ip = require('ip');
console.log('websocket server start.' + ' ipaddress = ' + ip.address() );
// const socket = io('http://localhost:5000');
httpsServer.listen(80, function () {
console.log('Server port: ' + port);
});
I have generated certificates and added them. On running the server it gives message of server started but does not load on browser.
Try adding these lines of code and see if you get "Hello" text in your browser.
https.get("/", (req, res) => {
res.send("Hello");
});
if that didn't work try doing it this way
httpsServer.get("/", (req, res) => {
res.send("Hello");
});
EDIT
Check out the official documentation https://nodejs.org/api/https.html

How can I adjust my NodeJs server code to responf to HTTPS requests?

I have a small nodejs server which is working without a problem. Now I am trying to make use of "HTTPS" for security reasons. I have the following code, but when I try to open the page in Firefox via link [http://192.168.2.22:8080/api/users], on the terminal I see DIRECTING >>> https://192.168.2.22:8080/api/users but in the browser, instead of the expected response, I encounter this error:
Secure Connection Failed An error occurred during a connection to
192.168.2.22:8080. SSL received a record that exceeded the maximum permissible length.
Error code: SSL_ERROR_RX_RECORD_TOO_LONG
// Modules /////////////////////////////////////////////////////////////////////
const db = require('./db.js');
// Packages ////////////////////////////////////////////////////////////////////
const colors = require('colors');
const express = require('express');
const app = express();
const fileUpload = require('express-fileupload');
const fs = require('fs');
// Constant Variables //////////////////////////////////////////////////////////
const PORT_SERVER = 8080;
const HOST = '192.168.2.22';
////////////////////////////////////////////////////////////////////////////////
app.use(express.json({limit: '50mb'}));
// app.use(express.urlencoded({limit: '50mb'}));
// set up a route to redirect http to https
app.get('*', function(req, res) {
console.log('DIRECTING >>> https ://' + req.headers.host + req.url);
res.redirect('https://' + req.headers.host + req.url);
});
app.get('/api/users/', async (req, res) => {
console.log('CHECK POINT !!!');
let users = await db.db.get_users();
console.log("USERS : " + users);
res.send(users);
});
// have it listen on 8080
app.listen(PORT_SERVER, () => console.log(`Listen at ${PORT_SERVER}...`));
How can I resolve this? I could not find a solution that I can easily apply to my code, I am kind of a newbie for NodeJs.
Thanks in advance
You have not configured your server for SSL. Configure SSL using the https module like below. In this example, I have created two express one for Http and one for https as we can not run both http and non https on same port.
const express = require('express');
const http = require('http'),
const https = require('https')
const fs = require('fs')
const httpApp = express()
const app = express()
const httpsOptions = {
key: fs.readFileSync("server.key"),
cert: fs.readFileSync("server.crt")
};
httpApp.set('port',80);
httpApp.get("*", function (req, res, next) {
res.redirect("https://" + req.headers.host + "/" + req.path);
});
app.set('port', 443);
app.enable('trust proxy');
http.createServer(httpApp).listen(httpApp.get('port'), function() {
console.log('Express HTTP server listening on port ' + httpApp.get('port'));
});
https.createServer(httpsOptions, app).listen(app.get('port'), function() {
console.log('Express HTTPS server listening on port ' + app.get('port'));
});
The Best way to redirect from non-http to https is to use Nginx web server as a reverse proxy and define redirection rule in Nginx config file.
client--->nginx reverse proxy(with SSL and redirection rules)-->express server
try changing the port to 443, https runs on 443 by default!

Making Node js apps https secure

I am creating a chaincode project , in which nodejs is consuming the chaincoe smartcontract.
My project structure includes index.js - swagger specs , app.js - to consumer swagger specs and bin/www - where http specification is defined .
I have defined http with basic auth and it works fine. For making all the services https secure , I have downloaded open ssl in my linux machine and have generated the certificate and the private key. (https://www.linuxhelp.com/how-to-install-and-update-openssl-on-ubuntu-16-04/)
I have made changes in the bin/www.js for the https part :
#!/usr/bin/env node
var app = require('../app');
var fs = require('fs');
var http = require('http');
var https = require('https');
require("dotenv").config();
var privateKey = fs.readFileSync('key.pem').toString();
var certificate = fs.readFileSync('cert.pem').toString();
var port = normalizePort(process.env.PORT || '8080');
app.set('port', port);
var hostname = process.env.HOSTNAME;
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
https.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write('Hello World!');
res.end();
}).listen(8080);
but this is not working . I have also imported the certificate and key in the mozilla. Request all to kindly help on this.
Thanks in advance.
You need to add the key and cert to the createServer function.
const options = {
key: fs.readFileSync('key.pem').toString();
cert: fs.readFileSync('cert.pem').toString();
}
https
.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
})
.listen(443, function(){
console.log("Server listening on localhost:443");
});
Now, as #aditi said in the comments, the callback in createServer is a request handler. That means it will trigger when there is a request event. A request event is triggered by mostly HTTP requesting the server. So, if you open localhost:443 it will show you the "hello world" text.
If you want to console log something when the server is started (listing) you need to add the callback in the listen function. Which you have done.
it worked ,
I used
https.createServer(httpsOptions,app)
.listen(port,function(){
console.log("Inside HTTPS creation");
})
Thanks all.

node.js with http and https routing

Currently I make my node.js project create both http and https server.
var httpServer = http.createServer(app);
httpServer.listen(3000);
var httpsOptions = {
ca: fs.readFileSync(config.https.ssl.ca, 'utf8'),
key: fs.readFileSync(config.https.ssl.key, 'utf8'),
cert: fs.readFileSync(config.https.ssl.cert, 'utf8')
};
var httpsServer = https.createServer(httpsOptions, app);
httpsServer.listen(8000);
Also I used this middleware to redirect all http traffic to https.
app.all('*', function(req, res, next){
var host = req.header("host");
if (host && host.indexOf('localhost') !== -1) {
next()
} else if (req.connection.encrypted) {
next();
} else {
res.redirect('https://' + host + req.url);
}
});
But for some pages I do not need https connections, say http://www.domain.com/shops/ route. Can I make this route use http method and all other routes use https still?
p.s: this page request resources from other routes like bower_components, public, ... etc.
You can find request path as follows, which might help you to redirect your request accordingly.
var url_parts = url.parse(req.url);
console.log(url_parts);
console.log(url_parts.pathname);

Intercept HTTP responses on an HTTPS server in Node.JS and Express

I'm just starting with node.js and express and I'm doing a simple HTTPS server. I've been working with nginx for some time and when I make an HTTP request to an HTTPS endpoint I get a "400 Bad Request" error. However, when using node.js the request never finishes.
How can I intercept an HTTP request in Express to be able to generate the "400 Bad Request" response?
This is my code:
var express = require('express');
var https = require('https');
var fs = require('fs');
var port = process.env.PORT || 8080;
var tls_options = {
key: fs.readFileSync('certs/server.key'),
cert: fs.readFileSync('certs/server.crt'),
ca: fs.readFileSync('certs/ca.crt'),
requestCert: true,
};
var app = express();
var router = express.Router();
router.get('/', function(req, res) {
res.json({ message: 'Checkpoint!!' });
});
app.use('/', router);
var secureServer = https.createServer(tls_options, app);
secureServer.listen(port);
console.log('Listening on port ' + port);
Until now the only thing I've been able to use is getting a 'connection' event every time a request arrives to the server:
secureServer.on('connection', function (stream) {
console.log('someone connected!');
});
Done. In fact, an HTTP request to an HTTPS socket ends after the default 120secs TLS handsahke timeout. This way I can end the request without waiting. I include the solution I used just for future references if anything needs the same functionality.
var secureServer = https.createServer(options, app);
secureServer.on('connection', function(socket) {
socket.on('data', function(data) {
var first_line = data.toString().split('\r\n')[0];
var pattern = /\bhttp\/1\.[01]$\b/i;
if (pattern.test(first_line)) {
var headers = {};
headers['Date'] = new Date().toUTCString();
headers['Connection'] = 'close';
var headers_string = '';
for (var name in headers) {
headers_string = headers_string + '\r\n' + name + ': ' + headers[name];
}
socket.end('HTTP/1.1 400 Bad Request' + headers_string);
}
});
There isn't a way of starting both HTTP and HTTPS servers on the same port. What most people do is either:
Start two servers (one HTTP and one HTTPS) on different ports, and redirect the HTTP traffic to HTTPS. Using Express it would mean the additional code:
// create two ports, one for HTTP and one for HTTPS
var port = process.env.PORT || 8080;
var httpsPort = 8081;
// redirect all HTTP requests to HTTPS
app.use(function(req, res, next) {
var hostname;
if (!req.secure) {
hostname = req.get("host").split(":")[0];
return res.redirect(["https://", hostname, ":", httpsPort, req.url].join(""));
}
next();
});
app.listen(port); // listen on HTTP
https.createServer(tls_options, app).listen(httpsPort); // listen on HTTPS
Or they use nginx or apache to handle outside connections (both HTTP and HTTPS) and redirect traffic to the Node server (which can then just run on HTTP).

Resources