Ubuntu | Nodejs | Secure location for SSL certificates? - node.js

I have a nodeStatic app on an ubuntu server.
A domain links to the server.
Now I need to add SSL certificates:
var fs = require('fs');
var options = {
key: fs.readFileSync('ssl/privatekey.pem').toString(),
cert: fs.readFileSync('ssl/certificate.pem').toString()
};
var fileServer = new(nodeStatic.Server)();
var app = http.createServer(options, function(req, res) {
fileServer.serve(req, res);
}).listen(80);
The thing is they are not secure in the folder with the app, or am I wrong?
Here is my folder structure:
The app is in var/www/myapp
Anyone could open the contents of my certificates in the webbrowser with example.com/ssl/privatekey.pem
What is a good practise to serve the certificates and keep everything secure? Put them on the same level as index.js, or even further above? Is this secure?
Bonus question:
Kind of a general question, but the server is fresh, what shall I configure to keep everything secure? A good link on the topic would do it.

Related

Express server not redirecting HTTP to HTTPS

I am attempting to setup a server with ExpressJS which uses HTTPS and servers a React app. I want any HTTP requests to be redirected to using HTTPS.
Additional constraint: I am using React router, so the server needs to be able to handle that. e.g. if I request localhost:3000/profile, I want React Router to handle that, I just need Express to server up index.html as I had gone to localhost:3000.
Problem: I think I've been able to setup HTTPS (Chrome complains but I don't mind for now), but I cannot get redirection to work.
For comparison, this is my code for how I setup my HTTP-only server for development (before I ever tried to setup HTTPS):
const express = require('express');
const http = require('http');
const path = require('path');
const app = express();
const DIST_DIR = path.resolve('./dist');
app.use(express.static(DIST_DIR));
app.get('*', (req, res) => {
res.sendFile(path.resolve(DIST_DIR, './index.html'));
});
const devServer = http.createServer(app);
devServer.listen(3000);
Next, I started with this guide. I created a self-signed SSL certificate then set up my application. I then looked at some examples of how to redirect, such as this question.
However, it doesn't seem to be working.
Here is my code at present:
app.use(express.static(DIST_DIR));
app.use((req, res, next) => {
if (req.secure) {
next();
} else {
res.redirect(`https://${req.headers.host}${req.url}`);
}
});
app.get('*', (req, res) => {
res.sendFile(path.resolve(DIST_DIR, './index.html'));
});
const httpServer = http.createServer(app);
httpServer.listen(3080);
const privateKey = // uses FS to get my key
const certificate = // uses FS to get my cert
const credentials = { key: privateKey, cert: certificate };
const httpsServer = https.createServer(credentials, app);
httpsServer.listen(3443);
I can access https://localhost:3443 and navigate the app as expected, and Express properly handles refreshes on pages like /profile. Great. Chrome complains that "CA root certificate is not trusted. Install this cert in the trusted root certification authorities store" but I haven't put in the work to solve that, because in a real production environment I'd be provided the certificate and key from a trusted source.
However, when I go to http://localhost:3080, I just end up at http://localhost:3080. Chrome devtools shows I'm not using HTTPS. Furthermore, I can't go directly to /profile, as Chrome gives me the error "This site can’t provide a secure connection".
I've tried other methods listed in that stackoverflow question I linked, but they either have the same behavior or straight up don't work. I'm a bit out of my element here and I'm trying to learn, but I don't understand why this isn't working. Any help would be appreciated. Thanks.
While you can manage this in your application it is often the convention to have a web server like nginix or apache in front of your application that manages the https redirection. Depending on your setup it is also common to manage your certificates at this front server to simplify certificate management. If you are going to deploy onto aws or another cloud provider I would let their infrastructure handle this for you.

Not able to secure website on Firefox even after installing SSL certificate

I am using the below code to install SSL certificate on my website:
const express = require('express');
const path = require('path');
const app = express();
const fs= require('fs');
let privateKey = fs.readFileSync('certificate/x.key', 'utf8');
let ca = fs.readFileSync('certificate/x.crt', 'utf8');
let certificate = fs.readFileSync('certificate/x.crt', 'utf8');
let credentials = { key: privateKey, cert: certificate, ca: ca };
const http = require('http');
const https = require('https');
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
let httpsServer = https.createServer(credentials, app);
httpsServer.listen(443);
This is a react app and I am serving it via Node.js.
When I open the website on Chrome or Microsoft Edge, it shows the connection as secure, encrypted and valid but when I open it on firefox, it shows that the connection is not secure.
What could be the problem?
If your intermediate certificate contains multiple blocks, then you should split them into different files and send them one-by-one as an array in ca parameter like this:
let credentials = { key: privateKey, cert: certificate, ca: [
fs.readFileSync('certificate/x_1.pem', 'utf8'),
fs.readFileSync('certificate/x_2.pem', 'utf8'),
fs.readFileSync('certificate/x_3.pem', 'utf8'),
[...]
] };
If your certificate looks correct on other Browsers and simply does not appear in Firefox, the reason is almost certainly, that Firefox still shows a cached certificate.
Simply clear Firefox's SSL state and you should be good to go:
On the History menu, click Clear Recent History. The Clear All History dialog appears.
In the Time range to clear list box, select Everything.
Select the Active Logins check box.
Click Clear Now.
Source:
https://www.a2hosting.co.uk/kb/getting-started-guide/internet-and-networking/clearing-a-web-browsers-ssl-state#Mozilla-Firefox
I suggest you to enter your site address here and test it for issues.
If you see, e.g. full chain not provided issue, you can create a full chain of all your certificates and PEM-encode it into a single cert file, then provide it as a cert file. You can even download this full chain there directly after the test, too.
Providing a full chain fixed issues for me several times, where everything worked fine in chrome, but not in some other cases.

Universal link not redirecting to app store

I am trying to implement setup Universal Link for IOS 9 device with both side configuration.Firstly I have done configuration at server Side with steps:
1.created one unsigned apple-app-site-association-unsigned.txt file and file contents is
{
"activitycontinuation": {
"apps": [
"9JA89QQLNQ.com.apple.wwdc"
]
},
"applinks": {
"apps": [],
"details": [
{
"appID":"9JA89QQLNQ.com.apple.wwdc",
"paths": [
"*",
"/"
]
}
]
}
}
2.Did sign the above mentioned file using
cat apple-app-site-association-unsigned.txt | openssl smime -sign
-inkey demo.key -signer demo.crt -certfile demo.pem -noattr -nodetach -outform DER >
apple-app-site-association
3.then it is created on signed file i.e apple-app-site-association
4.moved this file into root server where my certificates are available.
5.created one endpoint with node.js
var https = require('https');
var fs=require('fs');
var express = require('express');
var privateKey = fs.readFileSync(home_path + 'src/Server/API/demo.key', 'utf8');
var certificate = fs.readFileSync(home_path + 'src/Server/API/demo.crt', 'utf8');
var credentials = {key: privateKey, cert: certificate};
var app = express();
var httpsServer = https.createServer(credentials, app);
httpsServer.listen(8443);
console.log('deeplink service listening on port 3501');
var aasa = fs.readFileSync('./apple-app-site-association');
app.get('/apple-app-site-association', function(req, res, next) {
console.log("Request landed on 8443 port.");
res.set('content-type', 'application/pkcs7-mime');
res.status(200).send(aasa);
});
6.then my end point is:- https://domain.com:8443/apple-app-site-association
7.My app is not installed in device then if I copied universal link i.e https://domain.com:8443/apple-app-site-association in Ios 9 safari browser,it is not redirecting to App store, instead of that it is displaying apple-app-site-association file to download.
Note:- I did only server side configuration,so that if my app in not installed it should redirect to app store.
Any idea on this where I am wrong?
I believe you may be fundamentally misunderstanding how Universal Links work. You should do some additional research into Universal Links to make sure you have the concept correct. This might be a good place to start.
That said, a few things that may help:
You can't implement Universal Links on just one half of the equation. For anything to work, you need both the server and in-app components.
iOS is very particular about where it looks for the apple-app-site-association file. It will not check on port 8443. The easiest option is to use the standard HTTPS port (443), but assuming your signing attempts are valid, non-HTTPS should also work (port 80).
The URL https://domain.com/apple-app-site-association (extraneous port reference removed) itself is not a Universal Link. This is simply a validation file you need to host so that when you implement Universal Links in your app, iOS is able to confirm you own and control the domain in question.
Universal Links don't automatically handle redirection to the App Store. They simply control whether the app opens (if it is installed) or the website loads (if the app is not installed). The website that is loaded when the app is not installed needs to implement the App Store redirect.
Many apps don't even attempt to handle all of this complexity, especially since Universal Links are still only a partial solution to deep linking (they don't work in Facebook, Twitter, or any app using SFSafariViewController, amongst others), so you may want to look into an external link routing service. Branch.io (full disclosure: I'm on the Branch team) is a good option and handles all of these technical details for you.

Making requests to a node API from a different domain using HTTPS

I am serving a static page over HTTPS (https://example.com) that makes requests to a node API on a different domain (example-api.com).
My API is a standard express app using HTTP. Here's my setup code:
var express = require('express');
var app = exports.app = express();
var port = process.env.PORT;
exports.server = require('http').createServer(app).listen(port);
In the requests from my static page, I specify https://example-api.com as the URL. This works most of the time, but every once in a while (10% of the time?) Chrome errors out on the requests with:
net::ERROR_INSECURE_RESPONSE
Other users who've come across this issue (e.g. Failed to load resource: net::ERR_INSECURE_RESPONSE socket.io) seem to solve it by adding a credentials option to their createServer call, e.g.
var server = https.createServer(credentials, app)
So when I tried to implement this I came up with the following:
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
};
var express = require('express');
var app = exports.app = express();
exports.server = require('https').createServer(options, app).listen(port);
However this solution doesn't seem to work for me. When I try it the requests never make it to my app - even logs in app.use middleware don't appear.
What's really confusing is the fact that my setup seems to work most of the time.
Does anyone know how I can reliably make my requests?
Thanks and sorry in advance for my ignorance.
I struggled with this a bit as well. If you are on windows I have a solution that is a bit of a work around, but will allow you to serve your site, and NodeJS app over HTTPS.
In Windows, I created a reverse proxy in IIS to point at the nodeJS RESTful endpoint (i.e. nodeJS RESTful services == website.com:7000). Don't let reverse proxy scare you, its gravy.
To Implement:
Install IIS (if you haven't already)
Create your Self Signed Cert (assuming you know how to do that), or apply your Cert you are using now.
Install Application Request Routing
Open your website configuration, and go to URL Rewrite
For the rewrite stuff:
For Pattern: ^api(.*)
For rewrite: http://www.website.com:7000{R:1}
This basically takes any request from: https://www.website.com/api/someApiAwesomeness, and rewrites it to your nodejs App running at http://www.website.com:7000. Now you have an SSL RESTful app..
Good luck man I hope this helps!

NodeJS admin backend authentication strategy

I built a simple e-commerce site for a customer based in NodeJS/Express3/MongoDB and I have some doubts about what kind of authentication strategy to implement for the backend of the site.
I have little to no knowledge in authentication/authorization and I'm a little bit lost, so I'll appreciate if you help me clear the fog a little.
Only one person, the owner is going to access it. I implemented HTTP Simple authentication in the past, but as long as I know, the credentials are sent in plain text. I would need to implement HTTPS but I'm trying to avoid that.
I though about OAuth2 but it seems overkill for a single login.
Maybe Digest fits my needs. It doesn't send the credentials in plain text as long as I know.
I also found this library that look very well:
http://passportjs.org/
What do you recommend for this scenario?
I think Passport with local strategy is good enough.
Use HTTPS is always good, and not hard in Node.js.
Read The Web Application Hacker's Handbook if you wanna go deeper.
I suggest to use HTTP Basic or Digest authentication with HTTPS, you can do that using http-auth module.
// HTTPS module
var https = require('https');
// File system module.
var fs = require('fs');
// Authentication module.
var auth = require('http-auth');
var basic = auth.basic({
realm: "Simon Area.",
file: __dirname + "/../data/users.htpasswd" // gevorg:gpass, Sarah:testpass ...
});
// HTTPS server options.
var options = {
key: fs.readFileSync(__dirname + "/../data/server.key"),
cert: fs.readFileSync(__dirname + "/../data/server.crt")
};
// Starting server.
https.createServer(basic, options, function (req, res) {
res.end("Welcome to private area - " + req.user + "!");
}).listen(1337);

Resources