создание прямого https-прокси с использованием http-node-proxy

Я пытаюсь создать прямой прокси-сервер, способный также обрабатывать веб-сайты HTTPS. Я пытаюсь наблюдать и изменять трафик для разных сайтов. Это мой код, который работает для http-сайтов, но не для https-сайтов.

httpProxy.createServer(function(req, res, next) {
   //custom logic
   next();
}, function(req, res) {
   var proxy = new httpProxy.RoutingProxy();
   var buffer = httpProxy.buffer(req);
   var urlObj = url.parse(req.url);
   req.headers.host = urlObj.host;
   req.url = urlObj.path;
   console.log(urlObj.protocol);
  setTimeout(function() {
     proxy.proxyRequest(req, res, {
        host: urlObj.host,
        port: 80,
        buffer: buffer,
    }
   )}, 5000);

}).listen(9000, function() {
console.log("Waiting for requests...");
});

Спасибо за помощь ребята!


person everconfusedGuy    schedule 04.06.2013    source источник


Ответы (5)


Существуют параметры https, которые необходимо указать при обработке https-трафика. Вот что я делаю в своей настройке прокси.

var fs = require('fs'),
    httpProxy = require('http-proxy');

var proxyTable = {};

proxyTable['domain.com'] = 'localhost:3001';
proxyTable['domain.com/path'] = 'localhost:3002';
proxyTable['sub.domain.com'] = 'localhost:3003';

var httpOptions = {
    router: proxyTable
};

var httpsOptions = {
    router: proxyTable,
    https: {
        passphrase: 'xxxxxxx',
        key: fs.readFileSync('/path/to/key'),
        ca: fs.readFileSync('/path/to/ca'),
        cert: fs.readFileSync('/path/to/crt')}
};

httpProxy.createServer(httpOptions).listen(80);
httpProxy.createServer(httpsOptions).listen(443);

Документация для https также находится на их странице github.

https://github.com/nodejitsu/node-http-proxy

person Timothy Strimple    schedule 24.07.2013
comment
Итак, я запускаю прокси-сервер на своем локальном хосте, и мне также нужно прокси-сайты https. Означает ли это, что мне нужно создать самозаверяющий сертификат для моего локального хоста? - person everconfusedGuy; 25.07.2013
comment
Вам понадобится сертификат для любого SSL-сайта, который вы проксируете. В моем примере у меня есть подстановочный знак для домена.com, и все элементы прокси доступны как для http, так и для https. - person Timothy Strimple; 25.07.2013
comment
Допустим, я хочу проксировать ответ Facebook, изменить ответ и отправить его пользователю. Значит ли это, что мне нужен сертификат Facebook? Я хотел бы сделать это для всех https-сайтов, на которые пользователь может перейти. - person everconfusedGuy; 25.07.2013
comment
просто чтобы уточнить, я запускаю прокси-сервер node на своем локальном хосте и проксирую все запросы и ответы на мой сервер localhost, вручную устанавливая прокси-сервер на localhost и мой порт через системные настройки. - person everconfusedGuy; 25.07.2013
comment
Просто чтобы прояснить ваш сценарий ... вы настраиваете локальный прокси-сервер и изменяете файл hosts, чтобы интернет-запрос проходил через ваше локальное соединение и возвращался к намеченной цели? В этом случае да, вам понадобится сертификат, подписанный для Facebook.com. Самоподписанный сертификат зашифрует трафик, но Facebook не сможет расшифровать последующий запрос. Браузер также будет отображать страницы предупреждений/ошибок относительно сертификата. Все эти вещи предназначены для защиты от того, что вы пытаетесь сделать, что равносильно атаке «человек посередине». - person Timothy Strimple; 25.07.2013
comment
Неа. Извините, если я не был ясен. Мой текущий прокси-сервер просто перенаправляет запросы к намеченной цели, и я хочу изменить ответ, отправленный Facebook, скажем, добавить больше HTML, прежде чем он будет отображаться пользователю. В настоящее время я могу сделать это для сайтов, отличных от HTTPS. Ответы HTTPS не перехватываются прокси-сервером, и поэтому я не могу их изменить. - person everconfusedGuy; 25.07.2013
comment
Шаги, которые я сейчас предпринимаю; запустите «node proxy.js» в моем терминале. Перейдите к сетевым настройкам в Linux, установите прокси в ручной режим и установите порт на 9000 (из моего примера) и начните просматривать Интернет. Теперь все запросы и ответы (для сайтов без HTTPS) проходят через мой скрипт nodejs, где я изменяю отправленные ответы и отправляю их клиенту. - person everconfusedGuy; 25.07.2013
comment
Извините, не совсем понятно, чем это отличается от моего сценария выше. Вы не можете проксировать https-запросы на сторонние веб-сайты, не имея их сертификата ssl. Это намеренно разработано таким образом. Взгляните на этот ответ на StackExchange по безопасности: security.stackexchange.com/questions/8145/ - person Timothy Strimple; 25.07.2013
comment
давайте продолжим обсуждение в чате - person Timothy Strimple; 25.07.2013

Если вы просто делаете прямой прокси, вам нужно принять во внимание несколько вещей.

  • Обычный запрос НЕ запускается на прокси-сервере для HTTPS-запроса — вместо этого вы увидите HTTP CONNECT.

Вот поток последовательности, который вам нужно обработать.

  1. Событие CONNECT отправляется из браузера на прокси, указанный в разделе HTTPS. Вы найдете это здесь: http://nodejs.org/api/http.html#http_event_connect Обратите внимание, что это происходит через модуль HTTP, а не через соединение HTTPS.
  2. Вы создаете новое сокетное соединение с запрошенным доменом (или вашим сопоставленным доменом). [срвсокет]
  3. Вы ответите на сокет CONNECT с помощью 200
  4. Вы запишете полученный с событием CONNECT буфер в srvSocket, а затем объедините два сокета srvSocket.pipe(socket);
  5. socket.pipe(srvSocket);

Поскольку вы пытаетесь подделать запрошенный домен локально, вам понадобится еще несколько вещей.

  1. Вам потребуется создать корневой ЦС.
  2. Вам нужно будет импортировать этот сертификат в качестве доверенного органа в вашу ОС.
  3. Вы будете использовать этот сертификат для создания нового файла ключа/сертификата для доменов, к которым вы пытаетесь получить доступ.
  4. Сопоставленные хосты должны будут ответить соответствующим файлом ключа/сертификата, созданным на шаге 3 для КАЖДОГО домена, который вы сопоставляете.
person Angelo R.    schedule 30.07.2013

https://github.com/substack/bouncy

var bouncy = require('bouncy');

var server = bouncy(function (req, res, bounce) {
    if (req.headers.host === 'beep.example.com') {
    bounce(8001);
    }
    else if (req.headers.host === 'boop.example.com') {
        bounce(8002);
    }
    else {
        res.statusCode = 404;
        res.end('no such host');
    }
});
server.listen(8000);

Если вы укажете opts.key и opts.cert, для соединения будет установлен безопасный режим с использованием tls. Сделайте это, если вы хотите сделать https-маршрутизатор.

person Jason Livesay    schedule 25.07.2013

У нас может быть промежуточное программное обеспечение, как показано ниже.

request = require("request"); app.use(function (req, res, next) { request('http://anotherurl.that.serves/the/request').pipe(res); });

См. пример https://github.com/manuks/proxy.

person Manu    schedule 09.09.2014

По сути, под http-proxy npm находятся некоторые сетевые библиотеки, которые использует Node (в частности, http://nodejs.org/api/https.html и TLS). Несмотря на то, что мой Apache смог просто подключить меня к самозаверяющему сертификату без прокси-сервера, получив доступ к нему в моем браузере:

https://localhost:8002    

Вам необходимо установить центр сертификации, чтобы обойти ошибку «невозможно проверить листовую подпись» в узле (я использовал параметр SSLCACertificateFile). Затем вы получите «self_signed_cert_in_chain». Это привело к тому, что некоторые результаты Google показали, что npm отказались от самозаверяющих сертификатов, но я почти уверен, что это не касается Node.

В итоге некоторые люди указывают, что вы используете process.env.NODE_TLS_REJECT_UNAUTHORIZED или rejectUnauthorized в своем https-агенте. Если вы покопаетесь в источнике http-proxy, вы обнаружите, что он принимает опцию агента. Использовать это:

/**
 * Module dependencies
 */

// basic includes
express     = require('express');
fs          = require('fs');
http        = require('http');
https       = require('https');
httpProxy   = require('http-proxy');
require('child_process').spawn(__dirname+'/../../../dependencies/apache/bin/httpd.exe',['-f',__dirname+'/../../../dependencies/apache/conf/httpd-payments.conf']); 

var app     = module.exports = express();
app.set('port', process.env.PORT || 8001); // we sometimes change the port

// creates an output object for this particular request
//app.use(express.cookieParser(''));
//app.use(express.bodyParser());
//app.use(express.methodOverride());

proxy   = httpProxy.createProxyServer();
proxy.on('error', function (err, req, res) {
    console.log(err);
    res.send(500,err);
    res.end();
});

app.all('*',function(req,res,next) {
    var options = {
      hostname: '127.0.0.1',
      port: 8002,
      rejectUnauthorized: false,
      key: fs.readFileSync(__dirname+"/../../../deployment/server.key.pem"),
      cert: fs.readFileSync(__dirname+"/../../../deployment/server.crt.pem")
    };
    agent = new https.Agent(options);
    try {
        proxy.web(req,res, {
            target: "https://localhost:8002",
            proxyTimeout: 30,
            agent: agent
        });
    } catch(e) {
        // 500 error
        res.send(500,e);
    }
})

/** 
 * Start Server
 */

var options = {
  key: fs.readFileSync(__dirname+"/../../../deployment/server.key.pem"),
  cert: fs.readFileSync(__dirname+"/../../../deployment/server.crt.pem")
};
server  = https.createServer(options,app).listen(app.get('port'), function () {
  console.log('Running payments server on port ' + app.get('port'));
});
person mikewhit    schedule 10.10.2014