Подтвердить что ты не робот

CONNECT запрос на перенаправление HTTP-прокси по SSL-соединению?

Я пишу HTTP-прокси, и мне трудно понять некоторые детали запроса CONNECT по TLS. Чтобы получить лучшую картину, я экспериментирую с Apache, чтобы наблюдать, как она взаимодействует с клиентами. Это от моего виртуального хоста по умолчанию.

NameVirtualHost *:443
<VirtualHost>
  ServerName example.com
  DocumentRoot htdocs/example.com  
  ProxyRequests On
  AllowConnect 22
  SSLEngine on
  SSLCertificateFile /root/ssl/example.com-startssl.pem
  SSLCertificateKeyFile /root/ssl/example.com-startssl.key
  SSLCertificateChainFile /root/ssl/sub.class1.server.ca.pem
  SSLStrictSNIVHostCheck off
</VirtualHost>

Разговор между Apache и моим клиентом происходит следующим образом.

а. клиент подключается к example.com:443 и отправляет example.com в квитирование TLS.

б. клиент отправляет HTTP-запрос.

CONNECT 192.168.1.1:22 HTTP/1.1
Host: example.com
Proxy-Connection: Keep-Alive

с. Apache говорит HTTP/1.1 400 Bad Request. В журнале ошибок Apache говорится:

Hostname example.com provided via SNI and hostname 192.168.1.1
provided via HTTP are different. 

Похоже, что Apache не смотрит на заголовок Host, а не видит, что он существует, поскольку HTTP/1.1 требует его. Я получаю идентичное неудачное поведение, если клиент отправляет Host: foo. Если я сделаю HTTP-запрос example.com:80 без TLS, тогда Apache подключит меня к 192.168.1.1:22.

Я не совсем понимаю это поведение. Что-то не так с запросом CONNECT? Кажется, я не могу найти соответствующие части RFC, которые объясняют все это.

4b9b3361

Ответ 1

Не ясно, пытаетесь ли вы использовать Apache Httpd в качестве прокси-сервера, это объясняет код статуса 400, который вы получаете. CONNECT используется клиентом и отправляется на прокси-сервер (возможно, Apache Httpd, но обычно нет), а не на целевой веб-сервер.

CONNECT используется между клиентом и прокси-сервером до установления соединения TLS между клиентом и конечным сервером. Клиент (C) подключается к прокси (P) proxy.example.com и отправляет этот запрос (включая пустую строку):

C->P: CONNECT www.example.com:443 HTTP/1.1
C->P: Host: www.example.com:443
C->P:

Прокси открывает TCP-соединение с www.example.com:443 (P-S) и отвечает клиенту кодом состояния 200, принимая запрос:

P->C: 200 OK
P->C: 

После этого соединение между клиентом и прокси-сервером (C-P) остается открытым. Прокси-сервер передает все соединения C-P на P-S и обратно. Клиент обновляет свое активное (P-S) соединение с соединением SSL/TLS, инициируя квитирование на TLS на этом канале. Поскольку все теперь передано на сервер, это как если бы обмен TLS выполнялся непосредственно с www.example.com:443.

Прокси не играет никакой роли в рукопожатии (и, следовательно, с SNI). Рукопожатие TLS эффективно происходит непосредственно между клиентом и конечным сервером.

Если вы пишете прокси-сервер, все, что вам нужно сделать, чтобы позволить вашим клиентам подключаться к серверам HTTPS, читается в запросе CONNECT, делает соединение с прокси-сервером на конечном сервере (данное в CONNECT), отправьте клиенту с ответом 200 OK, а затем переместите все, что вы прочитали с клиента на сервер, и наоборот.

RFC 2616 рассматривает CONNECT как способ установить простой туннель (который он есть). Об этом больше говорится в RFC 2817, хотя остальная часть RFC 2817 (обновления до TLS в не-прокси-HTTP-соединении) редко используется.

Похоже, что вы пытаетесь установить соединение между клиентом (C) и прокси (P) через TLS. Это хорошо, но клиент не будет использовать CONNECT для подключения к внешним веб-серверам (если это не соединение с сервером HTTPS тоже).

Ответ 2

Из RFC 2616 (раздел 14.23):

Поле заголовка запроса хоста указывает хост и порт Интернета    номер запрашиваемого ресурса, полученный из оригинала    URI, предоставленный пользователем или ссылочным ресурсом (как правило, URL-адрес HTTP,    как описано в разделе 3.2.2). Значение поля Host должно ДОЛЖНО представлять    именования сервера происхождения или шлюза, предоставленного    исходный URL.

Я понимаю, что вам нужно скопировать адрес из строки CONNECT в строку HOST. В общем, адрес ресурса - 192.168.1.1, а тот факт, что вы подключаетесь через example.com, ничего не меняет с точки зрения RFC.

Ответ 3

Ты делаешь все правильно. Это Apache, у которого что-то не так. Поддержка CONNECT over TLS была добавлена ​​только недавно (https://issues.apache.org/bugzilla/show_bug.cgi?id=29744), и еще есть кое-что, что нужно сгладить. Проблема, с которой вы сталкиваетесь, является одним из них.

Ответ 4

Довольно редко можно увидеть метод CONNECT внутри TLS (https). Я действительно не знаю ни одного клиента, который это делает (и мне было бы интересно узнать, кто это делает, потому что я думаю, что это действительно хорошая функция).

Обычно клиент соединяется с http (простой tcp) с прокси-сервером и отправляет метод CONNECT (и заголовок узла) на хост: 443. Затем прокси сделает прозрачное соединение с конечной точкой, а затем клиент отправит квитирование SSL через.

В этом случае данные защищены ssl "от конца до конца".

Метод CONNECT на самом деле не указан, он зарезервирован только в HTTP RFC. Но, как правило, это довольно просто, поэтому он совместим. Метод определяет хост [: порт]. Хост: заголовок можно просто игнорировать. Могут потребоваться некоторые дополнительные заголовки аутентификации прокси. Когда тело соединения начинается, синтаксический анализ больше не должен выполняться прокси-сервером (некоторые из них, потому что проверяют правильное SSL-подтверждение).