Not able to secure website on Firefox even after installing SSL certificate - node.js

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.

Related

How to get Node.Js to allow me to provide client cert with smart card/cac

I am trying to build a CAC authentication system using node.js but am having trouble. I followed a few tutorials regarding setting up a https server and I can get that to work just fine; however, using the logic below, whenever I access my server:
https://localhost:3000
I am prompted to login with the auth button that is sent from the "/" route handler; however, it immediately defaults to the else clause:
res.status(401)
.send(`Sorry, but you need to provide a client certificate to continue.`)
Normally, when accessing a cac-enabled site, I a prompted to choose a smart card certificate, which allows me to login; however, in this case, I am not being prompted at all.
I found a similar question on SO but was not able to get anything to work from the article they referenced. They mentioned that I would have to set my 'ca' property on the https server to allow the specific CA of the CAC in question. I used my browser to locate my CAC certificates and exported my certificates and put them in the approved CA list but I still do not get any prompt.
I was initially using req.connection.getPeerCertificate() to prompt for certificates; however, I read that req.connection was deprecated in my verison of Node.JS (14.15.0) so I have also tried req.socket.getPeerCertificate(). The server seems to run just fine either way and I get no errors client-side or server-side (with exception to the 401 response that is sent from the server since it is not getting a valid certificate). Any and all insight would be greatly appreciated.
const fs = require('fs')
const express = require('express')
const https = require('https')
// Https options
const options = {
// Path to private key (created by openssl in createSelfSignedCert.bat)
key: fs.readFileSync('server_key.pem')
// Path to public key
,cert: fs.readFileSync('server_cert.pem')
// Indicate that https server should request client certificates
,requestCert: true
// Manually handle bad requests (no certs)
,rejectUnauthorized: false
//List of accepted/valid CA certs - just our own for now
,ca: [
fs.readFileSync('server_cert.pem')
,fs.readFileSync('cac52.cer')
,fs.readFileSync('cac_export.p7b')
]
//,ca: .
}
// Use express for routing
const app = express()
// Unprotected public endpoint
app.get('/', (req, res) => {
res.send('Auth')
})
// Protected endpoint
app.get('/login', (req, res) => {
// req.connection is deprecated, perhaps req.socket.getPeerCertificate()
const cert = req.connection.getPeerCertificate()
//const cert = req.socket.getPeerCertificate()
if (req.client.authorized) {
res.send(`Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!`)
console.log(`${cert}`)
} else if (cert.subject) {
res.status(403)
.send(`Sorry ${cert.subject.CN}, certificates from ${cert.issuer.CN} are not welcome here.`)
console.log(`${cert}`)
} else {
res.status(401)
.send(`Sorry, but you need to provide a client certificate to continue.`)
}
})
// Create https server with options and app routes
https.createServer(options, app).listen(3000)
I got this to work by changing this setting in my browser (Chrome):
chrome://flags/#allow-insecure-localhost
I got that from this answer. I think Chrome was not prompting me to choose a certificate because it was treating my dev environment as insecure. After I enabled that setting, the certificate prompt appeared as expected and the '/login' URL displayed the CN of the cert I chose.
Also, you'll need to make sure you have the correct CA's. AFAIK you cannot just export a CA certificate chain from your CAC - you need to have them provided to you from the CA. If you're working on a DoD project, you'll need the DoD Root CA's. If you already have a DoD CAC, you can get the DoD Root CA's from this link.
Remember, the certificates loaded on your CAC are client certificates, they are unique to you. The CA (Certifying Authority) is the organization that issued you those certificates. When you add the CA cert(s) to your project, what your app is effectively saying is "I can accept client certs issued by this CA."

Ubuntu | Nodejs | Secure location for SSL certificates?

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.

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.

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.

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