net::ERR_INSECURE_RESPONSE Angular 2 + Node.js + Socket.io - node.js

I am developing a web app with Angular 2, Node.js and socket.io. It is working perfectly fine with http, however I wanted to implement https protocol and I hit the wall.
I am getting net::ERR_INSECURE_RESPONSE on any http call from angular to my web api and also when trying to make a socket connection.
Here is what I did:
1) Generated my private key and csr.
2) Requested SSL certificate from Comodo using that csr.
3) Received 2 files from Comodo:
- domain.ca-bundle
- domain.crt
4) I copied those 2 files and my private key to a folder: /etc/apache2/ssl/
5) I modified default-ssl.conf in etc/apache2/sites-available to point to these files:
SSLCertificateFile /etc/apache2/ssl/domain.crt
SSLCertificateKeyFile /etc/apache2/ssl/mypriv.key
SSLCertificateChainFile /etc/apache2/ssl/domain.ca-bundle
plus added:
ServerName domain.io
ServerAlias www.domain.io
6) Enabled https and restarted apache. Verified using SSL analyzer (https://sslanalyzer.comodoca.com/) if my cert is properly set up. It confirmed that connection to my site is now secure, so I assume this part was done properly and now I needed to fix the app and the server to handle https.
7) This is how socket connection is created in angular:
this.socket = io.connect('https://server_ip' + ':3000', {
rejectUnauthorized: false });
This is gonna give me net::ERR_INSECURE_RESPONSE later on
8) Angular app is also making http requests to the server like this:
var headers = new Headers();
headers.append('Content-Type', 'application/json');
let options = new RequestOptions({ headers: headers });
return this.http.post('https://server_ip' + ':3000/register',
JSON.stringify(userModel), options)
.map(res => res.json())
This is gonna give me net::ERR_INSECURE_RESPONSE as well (in response to OPTIONS call)
9) Now on the server side, this is the relevant piece of code:
var https = require('https');
var options = {
ca: splitca('/etc/apache2/ssl/domain.ca-bundle'),
key: fs.readFileSync('/etc/apache2/ssl/mypriv.key'),
cert: fs.readFileSync('/etc/apache2/ssl/domain.crt')
};
var server = https.createServer(options, app);
let io = require('socket.io')(https);
server.listen(3000, () => {
logger.debug('server started on port');
})
Server starts up fine, but any call from angular results in net::ERR_INSECURE_RESPONSE response (tried chrome, mozilla and IE). Both server and angular app are running on the same IP.
Any idea what I might be doing wrong here? Thanks!

Related

NodeJs - Secure Web Socket and Client Connection

I need to convert an application with websocket in a secure-websocket. (under windows)
Im using nodeJs as websocket server and a simple html page to connect to it.
Searching on google and here, I found this approach:
Create a certificate and a key for server. I've followed this tutorial:
https://www.cloudinsidr.com/content/how-to-install-the-most-recent-version-of-openssl-on-windows-10-in-64-bit/
After creating a .key and a .pem, I'have modified my nodejs websocket server to introduce the certificate:
const httpsOptions = {
key: fs.readFileSync('./api/security/cert.key'),
cert: fs.readFileSync('./api/security/cert.pem')
}
this._http = require('http');
this._server = this._http.createServer(httpsOptions , function(req, res) { this.closeCurrentConnections(req,res)}.bind(this));
var serverConfig = {
server: this._server,
autoAcceptConnections: false
}
this._wsServer = new WebSocketServer(serverConfig);
The Websocket server seems up when I start the nodejs
Now, in the client page I had this code:
var websocket_server = "ws://localhost:8128";
var echo_service = new WebSocket(websocket_server,"echo-protocol");
[...]
I changed it with the following code calling this page over HTTPS instead of simple HTTP:
var websocket_server = "wss://localhost:8128";
var echo_service = new WebSocket(websocket_server,"echo-protocol");
[...]
I got an error on client page:
testing_page.html:283 WebSocket connection to 'wss://localhost:8128/' failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR
I think is due to missing certificate.
I've tried to import my previous create certificate on chrome but I cant import because chrome is especting a .crt and/or other format. I've tried to force .pem but it doens't work.
What Im missing?

nodejs pem generated openssl self signed certificate generation for intranet CIPHER_MISMATCH

I am using the module "pem" for nodejs && express to generate openssl self signed certificates for a demo webserver run over a local intranet.
The issue I am having is that when I attempt to load pages off the webserver I am receiving the error: "The client and server don't support a common SSL protocol version or cipher suite."
How would I be able to utilize pem ( or other ) in a way to allow me run my webserver over https via my intranet?
I am running/testing this on a ubtuntu machine and also testing on a windows machine. Both are generating the same error - the accessible machine over the intranet would be from the linux box. I am using nodejs 10 and tested on firefox, chrome, edge and safari
...
pem.createCertificate({ days: 365, selfSigned: true }, this.start);
...
start(err, keys) {
if (err) {
throw err
}
let server = https.createServer(app,
{ key: keys.serviceKey, cert: keys.certificate });
server.listen(port,
() => console.log(`API/NG running on https://localhost:${port}`)
);
}
According to the documentation of the pem module, the order of arguments is in reverse order, like follows:
var serverOptions = {
key: keys.serviceKey,
cert: keys.certificate
};
var app = express();
var server = https.createServer(serverOptions, app);

HTTPS TLS Settings in Node

I was looking through my codebase today, the portion which sets up the server and found the following lines:
var https = require('https');
https.globalAgent.options.secureProtocol = 'TLSv1_2_method';
function createHttpsServer(app) {
var https = require('https');
var fs = require('fs');
const options = {
secureProtocol: 'TLSv1_2_method',
// ...
};
var server = https.createServer(options, app);
return server;
}
It looked like code duplication to me and I am not sure why these do different things (or do they?).
A colleague of mine told me that the top one is for controlling TLS in HTTPS requests made from NodeJS, which in turn, gives us access to the https.agent which is used for all things related to client HTTP requests.
This was also compared to the ServicePointManager in the .NET world.
So do these methods both do different things? At some point, our code does:
var server = protocol === 'https' ? createHttpsServer(app) : createHttpServer(app);
Wouldn't that be using the same server at the end of the day?
var server = protocol === 'https' ? createHttpsServer(app) : createHttpServer(app);
The above line creates the same server, the only difference is if the protocol is 'https' it will run on HTTPS server (this require SSL certificate) whereas if the protocol is http it will run on HTTP server.

Issue SSL handshake with own https proxy on NodeJS

I try to work on my own HTTPS Proxy and I can't create my https server, during the connexion initialization, I have this error message "tlsClientError Error: 101057795:error:1407609B:SSL routines:SSL23_GET_CLIENT_HELLO:https proxy request:openssl\ssl\s23_srvr.c:400".
Below the node JS source code:
private runHttpsServer() {
const instance = this;
const cert = fs.readFileSync("./certificate/server.crt", "utf8");
const key = fs.readFileSync("./certificate/key.pem", "utf8");
const options : https.ServerOptions = {
cert: cert,
key: key
};
const httpsServer = https.createServer(options, (req, res)=>{
console.log("Request ...");
instance.handleRequest.call(instance, req, res);
});
httpsServer.on("tlsClientError", (err : Error, tlsSocket : TLSSocket)=>{
console.log("tlsClientError", err.stack);
});
httpsServer.listen(7001);
}
When I browse to "https://localhost:7001/" directly without my proxy, I have no SSL handshake error.
When I browse to other website throughout my proxy, I have this SSL Handshake error.
Someone have already encountered this issue ?
Some can help me ?
It looks like you have the wrong understanding of how proxying HTTPS works. It is not that the client will make a TLS connection to the proxy as you assume but instead the client will send a plain HTTP request with the method CONNECT to the proxy in order to make the proxy establish a tunnel to the target host. Then the client will upgrade this tunnel to TLS and thus get an end-to-end protection between client and final server.
On the wire it will look something like this (the exact message might slightly vary - see RFC 2817 for details):
Client ------------------------> Proxy
- create TCP connection
- send to proxy:
> CONNECT google.com:443 HTTP/1.0\r\n
> \r\n
Proxy -----------------------------> Server
- create TCP connection
Client <------------------------ Proxy
- send to client
< HTTP/1.0 200 Connection established\r\n
< \r\n
Client <------------------------(Proxy)-----------------------------> Server
use tunnel from client to server through proxy to
- make the end-to-end TLS handshake
- transfer the HTTP messages within the TLS connection
... SSL routines:SSL23_GET_CLIENT_HELLO:https proxy request: ...
This error message says essentially that your proxy expected the start of the TLS handshake (ClientHello) but instead got a https proxy request, i.e. CONNECT .... You need to fix your proxy to properly handle https proxy requests which work as I've described.
I also had the same problem. As mr. Steffen Ullrich answered we (me and you) have wrong understanding how proxying HTTPS work. Firstly youd need listen event CONNECTION in http then doing tls handshake. In code it look likes so:
http
.createServer((req, res) => requestHandler(req, res, false))
.on('connect', (req, cltSocket, head) => {
const srvUrl = url.parse(`http://${req.url}`);
const srvSocket = net.connect(
srvUrl.port,
srvUrl.hostname,
() => {
cltSocket.write('HTTP/1.1 200 Connection Established\r\n'
+ 'Proxy-agent: Node.js-Proxy\r\n'
+ '\r\n');
srvSocket.write(head);
srvSocket.pipe(cltSocket).on('error', (e) => console.log('srvSocket', e));
cltSocket.pipe(srvSocket).on('error', (e) => console.log('cltSocket', e));
},
);
})
.listen(9000, () => console.log('HTTP Server started listening on port 9000'));
you can learn about it in official site of Node.js
Full code of my https proxy is available here.

Multiple SSL Certificates and HTTP/2 with Express.js

Scenario:
I have an express.js server which serves variations of the same static landing page based on where req.headers.host says the user is coming from - think sort of like A/B testing.
GET tulip.flower.com serves pages/flower.com/tulip.html
GET rose.flower.com serves pages/flower.com/rose.html
At the same time, this one IP is also responsible for:
GET potato.vegetable.com serving pages/vegetable.com/potato.html
It's important that these pages are served FAST, so they are precompiled and optimized in all sorts of ways.
The server now needs to:
Provide separate certificates for *.vegetables.com, *.fruits.com, *.rocks.net
Optionally provide no certificate for *.flowers.com
Offer HTTP2
The problem is that HTTP2 mandates a certificate, and there's now multiple certificates in play.
It appears that it's possible to use multiple certificates on one Node.js (and presumably by extension Express.js) server, but is it possible to combine it with a module like spdy, and if so, how?
Instead of hacking node, would it be smarter to pawn the task of sorting out http2 and SSL to nginx? Should the caching network like Imperva or Akamai handle this?
You can use also tls.createSecureContext, Nginx is not necassary.
MY example here:
const https = require("https");
const tls = require("tls");
const certs = {
"localhost": {
key: "./certs/localhost.key",
cert: "./certs/localhost.crt",
},
"example.com": {
key: "./certs/example.key",
cert: "./certs/example.cert",
ca: "./certs/example.ca",
},
}
function getSecureContexts(certs) {
if (!certs || Object.keys(certs).length === 0) {
throw new Error("Any certificate wasn't found.");
}
const certsToReturn = {};
for (const serverName of Object.keys(certs)) {
const appCert = certs[serverName];
certsToReturn[serverName] = tls.createSecureContext({
key: fs.readFileSync(appCert.key),
cert: fs.readFileSync(appCert.cert),
// If the 'ca' option is not given, then node.js will use the default
ca: appCert.ca ? sslCADecode(
fs.readFileSync(appCert.ca, "utf8"),
) : null,
});
}
return certsToReturn;
}
// if CA contains more certificates it will be parsed to array
function sslCADecode(source) {
if (!source || typeof (source) !== "string") {
return [];
}
return source.split(/-----END CERTIFICATE-----[\s\n]+-----BEGIN CERTIFICATE-----/)
.map((value, index: number, array) => {
if (index) {
value = "-----BEGIN CERTIFICATE-----" + value;
}
if (index !== array.length - 1) {
value = value + "-----END CERTIFICATE-----";
}
value = value.replace(/^\n+/, "").replace(/\n+$/, "");
return value;
});
}
const secureContexts = getSecureContexts(certs)
const options = {
// A function that will be called if the client supports SNI TLS extension.
SNICallback: (servername, cb) => {
const ctx = secureContexts[servername];
if (!ctx) {
log.debug(`Not found SSL certificate for host: ${servername}`);
} else {
log.debug(`SSL certificate has been found and assigned to ${servername}`);
}
if (cb) {
cb(null, ctx);
} else {
return ctx;
}
},
};
var https = require('https');
var httpsServer = https.createServer(options, (req, res) => { console.log(res, req)});
httpsServer.listen(443, function () {
console.log("Listening https on port: 443")
});
If you want test it:
edit /etc/hosts and add record 127.0.0.1 example.com
open browser with url https://example.com:443
Nginx can handle SSL termination nicely, and this will offload ssl processing power from your application servers.
If you have a secure private network between your nginx and application servers I recommend offloading ssl via nginx reverse proxy. In this practice nginx will listen on ssl, (certificates will be managed on nginx servers) then it will reverse proxy requests to application server on non ssl (so application servers dont require to have certificates on them, no ssl config and no ssl process burden).
If you don't have a secure private network between your nginx and application servers you can still use nginx as reverse proxy via configuring upstreams as ssl, but you will lose offloading benefits.
CDNs can do this too. They are basically reverse proxy + caching so I dont see a problem there.
Good read.
Let's Encrypt w/ Greenlock Express v3
I'm the author if Greenlock Express, which is Let's Encrypt for Node.js, Express, etc, and this use case is exactly what I made it for.
The basic setup looks like this:
require("greenlock-express")
.init(function getConfig() {
return {
package: require("./package.json")
manager: 'greenlock-manager-fs',
cluster: false,
configFile: '~/.config/greenlock/manager.json'
};
})
.serve(httpsWorker);
function httpsWorker(server) {
// Works with any Node app (Express, etc)
var app = require("./my-express-app.js");
// See, all normal stuff here
app.get("/hello", function(req, res) {
res.end("Hello, Encrypted World!");
});
// Serves on 80 and 443
// Get's SSL certificates magically!
server.serveApp(app);
}
It also works with node cluster so that you can take advantage of multiple cores.
It uses SNICallback to dynamically add certificates on the fly.
Site Management
The default manager plugin uses files on the file system, but there's great documentation on how to build your own.
Just to get started, the file-based plugin uses a config file that looks like this:
~/.config/greenlock/manager.json:
{
"subscriberEmail": "letsencrypt-test#therootcompany.com",
"agreeToTerms": true,
"sites": [
{
"subject": "example.com",
"altnames": ["example.com", "www.example.com"]
}
]
}
Very Extensible
I can't post all the possible options here, but it's very small and simple to start with, and very easy to scale out with advanced options as you need them.

Resources