Search code examples
node.jssslhttpsproxyman-in-the-middle

Communication between client and proxy keeps failing because of self-signed certificate


I have a proxy application written in Javascript/Node.js that uses the http-mitm-proxy library.

I also have an https server on a separate machine that will serve files (currently it just replies "hello world"). I used a self signed root CA and a self-signed certificate for this (details below).

On a third machine, I try to access the https file server via the proxy. I just use Firefox for this (I've configured the proxy settings with my proxy machine's IP).

What I'm trying to do is intercept the https traffic (i.e. the files) on the proxy and cache it (i.e. decrypt it, store it locally and then pass it on). This is basically a Man-In-The-Middle attack.

The problem is that the proxy and client keep rejecting the self-signed certificate during communication.

I generated the rootCA and self-signed certificate using the steps found here. I can provide the exact values/details, if necessary.

I imported the certificate in Firefox on my client machine and I use them in the proxy app code. I also added the appropriate entries in the hosts file on both client and proxy machine.

Here is my code:

// HTTPS File Server:

const https = require('https');
const fs = require('fs');

const options = {
  key:  fs.readFileSync('myupdateproxy.com.key'),
  cert: fs.readFileSync('myupdateproxy.com.crt')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(8000);

// proxy server code:

'use strict';

var port = 8081;

var path = require('path');
var Proxy = require('http-mitm-proxy');
var proxy = Proxy();


const fs = require('fs');

var chunks1 = [];
var chunks2 = [];

proxy.onCertificateRequired = function(hostname, callback) {
  return callback(null, {
    keyFile:  path.resolve('myupdateproxy.com.key'),
    certFile: path.resolve('myupdateproxy.com.crt')
  });
};

proxy.onError(function(ctx, err, errorKind) {
  // ctx may be null
  var url = (ctx && ctx.clientToProxyRequest) ? ctx.clientToProxyRequest.url : '';
  console.error(errorKind + ' on ' + url + ':', err);
});

proxy.onRequest(function(ctx, callback) {
  console.log('onRequest');
  callback();
});

proxy.onRequestData(function(ctx, chunk, callback) {
  console.log('onRequestData');
  chunks1.push(chunk);
  callback(null, chunk);
});

proxy.onRequestEnd(function(ctx, callback) {
  if (ctx.clientToProxyRequest.socket.remoteAddress !== undefined && 
      ctx.proxyToServerRequest.socket.remoteAddress !== undefined &&
      (Buffer.concat(chunks1)).length > 0)
      {
        console.log('From: ' + ctx.clientToProxyRequest.socket.remoteAddress);
        console.log('To: '   + ctx.proxyToServerRequest.socket.remoteAddress);
        console.log('Size: ' + (Buffer.concat(chunks1)).length);
        console.log('');
      }

  chunks1 = [];

  callback();
});

proxy.onResponse(function(ctx, callback) {
  callback(null);
});

proxy.onResponseData(function(ctx, chunk, callback) {
  chunks2.push(chunk);  
  callback(null, chunk);
});

proxy.onResponseEnd(function(ctx, callback) {
  var total_size=(Buffer.concat(chunks2)).length;

  if (ctx.serverToProxyResponse.socket.remoteAddress !== undefined && 
      ctx.proxyToClientResponse.socket.remoteAddress !== undefined &&
      total_size > 0)
  {
    console.log('From: ' + ctx.serverToProxyResponse.socket.remoteAddress);
    console.log('To: ' + ctx.proxyToClientResponse.socket.remoteAddress);
    console.log('Size: ' + total_size);
    console.log('');
  }

  console.log((Buffer.concat(chunks2)).toString());
  chunks2 = [];  
  callback();
});

proxy.listen({ port: port, sslCaDir: "/home/user/mycerts/" });
console.log('listening on ' + port);

I keep getting this error in the proxy app:

onRequest
PROXY_TO_SERVER_REQUEST_ERROR on /: Error: self signed certificate
    at TLSSocket.onConnectSecure (_tls_wrap.js:1473:34)
    at TLSSocket.emit (events.js:311:20)
    at TLSSocket._finishInit (_tls_wrap.js:916:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:686:12) {
  code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
}

And this message on the client machine (in Firefox):

PROXY_TO_SERVER_REQUEST_ERROR: Error: self signed certificate

Does anyone know what I'm doing wrong and how I can make this work?

Thanks!


Solution

  • Try to make insecure TLS connection from the proxy to the app - add this snippet to your proxy code:

    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
    

    Or try to add your custom CA cert to your system (where proxy app is running) CA certs. Exact command depends on used OS.