Do I need to allow only https in my backend server ? [Nodejs] - node.js

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 ?

Related

Node.js Express Websocket clients coming from http origin?

I have a webapp that communicates to Node.js Express server using websocket.
When verifying the websocket connection, I check the ORIGIN header of the request (and a few other parameters to ensure they are legitimate)
The expected request is either "https://www.mywebsite.com" or "https://mywebsite.com"
If the ORIGIN header is not expected, we will kick the user.
Then I noticed some people can be kicked when their socket connection looks alright, but the ORIGIN is "http://mywebsite.com". We quickly checked and realise the website can be visited in http. We added a piece of redirect code like this:
const server = express()
.enable('trust proxy')
.use((req, res, next) => {
req.secure ? next() : res.redirect('https://' + req.headers.host + req.url)
})
And now theoretically, whoever visit the http version of the website should be redirected to https.
But, even this redirection is done, we still notice people being kicked because their origin is http instead of https. Why is this so? Is there any chance that some users can never use https?
This is the correct way to redirect to https on Heroku:
Under the hood, Heroku router (over)writes the X-Forwarded-Proto and the X-Forwarded-Port request headers. The app must check X-Forwarded-Proto and respond with a redirect response when it is not https but http.
Taken from: https://help.heroku.com/J2R1S4T8/can-heroku-force-an-application-to-use-ssl-tls
This is some sample code you can use:
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
res.redirect(`https://${req.header('host')}${req.url}`)
} else {
next()
}
})
The reason your code doesn't work is that Heroku does SSL termination for you and serves the certificates this means the connection between the Heroku router and your Node.js server is insecure and req.secure returns false:
https://devcenter.heroku.com/articles/http-routing#routing
Correction: Cause you set trust proxy this means req.protocol will be set to https and req.secure will return true so your code will work.

How to ensure HTTP trigger will be called only via HTTPS?

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.

Proxy HTTPS with HTTP in Node.js express

I wrote an express app as an HTTP proxy, to intercept and analyse some of the network traffic. The parts of traffic my app is interested in are all HTTP, however I still want my app to proxy HTTPS so users can use it without extra setting.
My express app is created with a HTTP server. When testing, I changed the proxy setting in Chrome with SwitchyOmega, to proxy HTTPS connections with HTTP. HTTP works well, But my express app couldn't get these proxy requests for HTTPS.
So I wrote a simple TCP proxy to check on them, and find that they're like this:
CONNECT HOSTNAME:443 HTTP/1.1
Host: HOSTNAME
Proxy-Connection: keep-alive
User-Agent: MY_AGENT
ENCRYPTED HTTPS
I believe these requests are HTTP, but why express isn't receiving them?
For sure if I change the browser proxy setting to ignore HTTPS, the app works well. But I do want to know if there is any workaround that I can use to proxy all protocols with HTTP and only one port.
THX.
UPDATE- code from my express app
app.use('*', function (req, res, next) {
// print all the request the app receive
console.log('received:', req.url)
})
app.use(bodyParser.text({type: '*/*'}))
app.use(cookieParser())
app.use(logger('dev'))
app.use(express.static(path.join(__dirname, 'public')))
// serve web pages for my app, only the request targeting my server
// is handled here(right IP and port), proxy request gets handled after this.
app.use('/', internalRoute)
// analyse the part I want
app.use('/END_POINT_I_WANT', myRoute)
// handle proxy requests
app.use('*', function (req, res, next) {
// proxy the request here
})
The problem is, my first middleware, which is used to display all the requests the app receive, can't catch the HTTPS proxy requests wrapped in HTTP described above. And of course the middleware I used as proxy can't catch them either.
UPDATE-tried node-http-prxoy, no luck
var httpProxy = require('http-proxy')
, http = require('http')
, fs = require('fs')
var options = {target: 'http://127.0.0.1:8099'}
, proxy = httpProxy.createServer(options)
http.createServer(function (req, res) {
console.log(req.url)
proxy.web(req, res)
}).listen(5050)
With the above code, and browser setting to proxy all protocols with HTTP, it works the same as my express app. HTTPS proxy requests gets ERR_EMPTY_RESPONSE, and nothing on the console.
With the below options, it seems that I have to change the proxy protocol to HTTPS, which I'd rather not use, at least for now. And I get ERR_PROXY_CERTIFICATE_INVALID for my self-signed certs...
var options = { secure: true
, target: 'http://127.0.0.1:8099'
, ssl: { key: fs.readFileSync('cert/key.pem', 'utf8')
, cert: fs.readFileSync('cert/server.crt', 'utf8')
}
}
UPDATE- pin point the problem to the 'connect' event listener
Through some searching, I found this post helpful.
It pointed out that the http server doesn't have a listener for the connect event. I tried the code in the post, works. But as the last comment of that post mentioned, my app serves as a proxy in order to get the data, it then proxy the request to another proxy in order to go over the GreatFireWall.
The process is like : BROWSER -> MY_APP -> ANOTHER_PROXY -> TARGET.
Without the ANOTHER_PROXY, which is an HTTP proxy, it works well for both HTTP and HTTPS. However I failed to chain them all up. The ANOTHER_PROXY I use supports HTTPS over HTTP.
It's hard to see what might be wrong, since you haven't posted any code.
However, if you just want to create a simple proxy that supports HTTP and HTTPS, i think that you should consider using a module like node-http-proxy.
Their readme has example code for the most common scenarios, and it sounds like it will support your needs fine.

Disable HTTP completely in Heroku

We have a node.js express application running in Heroku. It handles authentication and has to be a highly secure.
We have forced redirect to HTTPS when we get HTTP request. But this does not seem to be enough. With tools like sslstrip we can POST via HTTP.
The only solution at hand seems to be disable the HTTP completely on Heroku.
How to do that? Is there any other suggestions?
According to OWASP you should not redirect from HTTP to HTTPS. See https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-REMOVED-_Do_Not_Perform_Redirects_from_Non-TLS_Page_to_TLS_Login_Page for more details.
I think the better solution would be to reject the request with a message letting the user know why. You should be able to do this in a middleware function. The actual status code you return is debatable but something like this should work:
app.use(function(req, res, next) {
if(req.protocol !== 'https') {
return res.status(403).send({message: 'SSL required'});
}
// allow the request to continue
next();
});
You can test whether a request used https and then force a redirect using https if that is required (note the concern pointed out by #Ryan about redirecting and security). With Heroku, you can check the req headers' x-forwarded-proto header to make sure it is https. Here is an example:
var express = require('express');
var env = process.env.NODE_ENV || 'development';
var forceSSL = function (req, res, next) {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(['https://', req.get('Host'), req.url].join(''));
}
return next();
};
var app = express();
// in your app-level configurations
if (env === 'production') app.use(forceSSL);
Note: the Heroku load balancers are determining the x-forwarded-proto header before it hits your app.
Also: get an SSL certificate if you are using a custom domain with Heroku
We've used express-sslify npm package. Added
app.use(enforce.HTTPS(true));

how can I check that a request is coming over https in express

I want force certain routes to always use a secure connection in my express app. How can I check to make sure it is using https?
I am using piggyback ssl on heroku for my deployments.
I deploy on Heroku as well. They add a bunch of their headers when they use nginx to reverse proxy. The one of interest in this case would be x-forwarded-proto.
This is what I did:
app.get(/\/register$/, function(req, res){
console.log(JSON.stringify(req.headers)); //to see all headers that heroku adds
if(req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'] === "http") {
res.redirect("https://" + req.headers.host + req.url);
}
else {
//the rest of your logic to handle this route
}
});
app.enable('trust proxy');
"Using Express behind a reverse proxy such as Varnish or Nginx is trivial, however it does require configuration. By enabling the "trust proxy" setting via app.enable('trust proxy'), Express will have knowledge that it's sitting behind a proxy and that the X-Forwarded-* header fields may be trusted, which otherwise may be easily spoofed."
Express behind proxies doco
In order to run a secure server (https) it would have to be created independently from a non-secure server (http). They would also listen on separate ports. Try something like this:
var express = require('express)
, app_insecure = express.createServer()
, app_secure = express.createServer({ key: 'mysecurekey' })
app_insecure.get('/secure-page',function(req, res){
// This is an insecure page, redirect to secure
res.redirect('https://www.mysecuresite.com/secure-page')
})
app_secure.get('/secure-page', function(req,res){
// Now we're on a secure page
})
app_insecure.listen(80)
app_secure.listen(443)
OR this could be implemented as route middleware
var redirect_secure = function(req, res, next){
res.redirect('https://mysite.com' + req.url)
}
app_insecure.get('/secure-page',redirect_secure,function(req, res){})
Now you would only have to include the function reference: redirect_secure() on the paths that you would like redirected to a secure location.

Resources