I want to allow only https traffic to my Hapi Js server.
In this thread:
Node.JS, Express and Heroku - how to handle HTTP and HTTPS?
it was accomplished by:
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();
}
});
}
This is a good npm module for this: https://www.npmjs.org/package/hapi-require-https
This is my (fairly hacky) solution:
// redirect all http request to secure route
if ('production' === process.env.NODE_ENV) {
server.ext('onRequest', function (request, next) {
if (request.headers['x-forwarded-proto'] !== 'https') {
request.originalPath = request.path;
request.setUrl('/redirect');
}
next();
});
server.route([{
method: 'GET',
path: '/redirect',
handler: function (request, reply) {
var host = request.headers.host;
reply().redirect('https://' + host + request.originalPath);
}
}]);
}
Maybe someone can come up with something cleaner.
Warning: This does not prevent insecure http traffic. It just redirects the browsers to https locations.
Related
I have an express app set up using http-proxy-middleware, but I'm having a problem routing only a subset of requests through the proxy.
Here is what my config looks like:
app.use(/\/.*-img/i, proxy({changeOrigin: true, logLevel: 'debug', target: 'http://ucassets.blob.core.windows.net'}));
app.get('*', (req, res) => {
const location = req.url;
const memoryHistory = createMemoryHistory(req.originalUrl);
const store = configureStore(memoryHistory);
const history = syncHistoryWithStore(memoryHistory, store);
match({history, routes, location},
(error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
/* render HTML and send code 200 */
} else {
res.status(404).send('Not Found?');
}
});
});
The proxy route sometimes works, and other times ends up printing that "Not found?" 404 result. Of note, there is NOT a route in my react-router config that matches the proxy route above. Since both routes technically match the express route, I guess express is randomly choosing which one to execute?
EDIT: Here's an example route that is being problematic:
/user-img/bc070850-11c9-a79e-2881-9bd6cc04c3ca.jpg
I am proxying my api via following setup in my express config
// Proxy api calls
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})
config.API_HOST in here resolves to my api url and req.url is some endpoint i.e. /users I tried following documentation on npm for request and set up my headers like so
// Proxy api calls
app.use('/api', function (req, res) {
let options = {
url: config.API_HOST + req.url,
options: { 'mycustomheader': 'test' }
}
req.pipe(request(options)).pipe(res)
})
But I am not able to see my custom headers in chrome dev tools under Network.
Was able to achieve it this way
app.use('/api', function (req, res) {
let url = config.API_HOST + req.ur
req.headers['someHeader'] = 'someValue'
req.pipe(request(url)).pipe(res)
})
I have node app running on express. I'd like to set up two different account types (one for buyers, one for sellers) and have them accessible through different subdomains (ie buyers.example.com and sellers.example.com). I'm not sure how to set this up locally. I know that I can edit the hosts file on my machine to have *.localhost resolve to 127.0.0.1, but that doesn't solve my problem. In that case, buyers.localhost/login and sellers.localhost/login will route to the same place. Is this a common issue, and if so, what is the standard way of handling this? What are some options for me to separate the logic for handling two account types?
First add this:
app.get('*', function(req, res, next){
if (req.headers.host == 'buyers.localhost:5000') { //Port is important if the url has it
req.url = '/buyers' + req.url;
}
else if(req.headers.host == 'sellers.localhost:5000') {
req.url = '/sellers' + req.url;
}
next();
});
And then:
app.get('/login', function(){
//Default case, no subdomain
})
app.get('/buyers/login', function(){
//Buyers subdomain
})
app.get('/sellers/login', function(){
//Sellers subdomain
})
Learned this here: https://groups.google.com/forum/#!topic/express-js/sEu66n8Oju0
app.all("*", function (req: Request, res: Response, next: NextFunction) {
if (req.headers.host == `v1.${process.env.SERVER_DOMAIN}`) req.url = `/v1${req.url}`; // ? <= direct from v1 to domain/v1
if (req.headers.host == `api.${process.env.SERVER_DOMAIN}`) req.url = `/api${req.url}`; // ? <= direct from v1 to domain/api
next();
}); //Port is important if the url has it
I am using the following to redirect all http requests to https requests.
I can see from logs that the header 'x-forwarded-proto' is never populated and is undefined.
app.get('*', function(req, res, next) {
//http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-proto
if (req.headers['x-forwarded-proto'] != "https") {
res.redirect('https://' + req.get('host') + req.url);
} else {
next();
}
});
It is causing a redirect loop. How can I redirect properly without looping?
edit:
my original answer below is for express 3.x, for 4.x you can get a string http or https in req.protocol, thx #BrandonClark
use req.get, not req.headers. Note that POST requests and all other non-GET will not see this middleware.
It's also possible that Express does not carry the x-forwarded-proto header across when you redirect. You may need to set it yourself.
app.get('*', function(req, res, next) {
//http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-proto
if (req.get('x-forwarded-proto') != "https") {
res.set('x-forwarded-proto', 'https');
res.redirect('https://' + req.get('host') + req.url);
} else {
next();
}
});
Another way to force https:
function ensureSecure(req, res, next){
if(req.secure){
// OK, continue
return next();
};
res.redirect('https://'+req.host+req.url); // handle port numbers if non 443
};
app.all('*', ensureSecure);
You can edit the nginx config file in the EC2 instance. SSH to ec2 instance and follow the following steps
go to /etc/nginx/conf.d
open 00_elastic_beanstalk_proxy.conf
sudo vi 00_elastic_beanstalk_proxy.conf
put
location / {
if ($http_x_forwarded_proto != 'https') {
rewrite ^ https://$host$request_uri? permanent;
}
…
}
reload nginx
sudo /usr/sbin/nginx -s reload
I'm using expressjs with node and running both https and http.
I want to require that all routes for /secure/* use https. This is done:
app.all("/secure/*", function(req, res, next) {
if (!req.connection.encrypted) {
res.redirect("https://" + req.headers["host"].replace(new RegExp(config.http_port, "g"), config.https_port) + req.url);
} else {
return next();
};
});
However, I also want to require that all routes that are not using /secure/* and try to access https, are redirected using the same method to http.
I tried doing this:
app.all("*", function(req, res, next) {
console.log(req);
if (req.connection.encrypted) {
res.redirect("http://" + req.headers["host"].replace(new RegExp(config.https_port, "g"), config.http_port) + req.url);
} else {
return next();
};
});
But I end up in a redirect loop when accessing the https pages. Is there a way to specify all routes, except those with /secure/* ?
Thank you!
A simple solution to your problem is:
app.all("*", function(req, res, next) {
if (req.connection.encrypted && !/^\/secure/.test(req.url)) {
res.redirect("http://" + req.headers["host"].replace(new RegExp(config.https_port, "g"), config.http_port) + req.url);
} else {
return next();
};
});
Only do the redirect if the URL doesn't start with /secure.
However, I'd propose that instead of the redundant 'secure' label in the URLs, just mark certain paths as requireHTTP or requireHTTPS. You know you can pass multiple methods into app.get and other such router methods, right? Assuming you define requireHTTP and requireHTTPS (which would be identical to your original functions), you'd just do:
app.get("/path/to/keep/encrypted", requireHTTPS, function(req, res) {
// Rest of controller
});
app.get("/path/to/keep/public", requireHTTP, function(req, res) {
// Rest of controller
});
app.get("/path/you/dont/care/about/encryption/status", function(req, res) {
// Rest of controller
});
That should do it.