web-dev-qa-db-ja.com

SSL接続を介したフォワードHTTPプロキシへのCONNECTリクエスト?

HTTPプロキシを作成していますが、TLSを介したCONNECTリクエストの作成に関する詳細を理解できません。より良い状況を得るために、私は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と私のクライアントの間の会話はこのようになります。

a。クライアントはexample.com:443に接続し、TLSハンドシェイクでexample.comを送信します。

b。クライアントはHTTPリクエストを送信します。

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

c。 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. 

HTTP/1.1がそれを要求するので、Apacheはそこにあることを確認する以外にホストヘッダーを調べないようです。クライアントがHost: fooを送信した場合、同じ動作が失敗します。 TLSなしでexample.com:80にHTTPリクエストを送信すると、Apacheは192.168.1.1:22に接続します。

私はこの振る舞いを完全に理解していません。 CONNECTリクエストに問題がありますか?このすべてを説明するRFCの関連部分を見つけることができないようです。

14
sigjuice

Apache Httpdをプロキシサーバーとして使用するかどうかは明らかではありません。これは、取得する400ステータスコードの説明になります。 CONNECTはクライアントによって使用され、宛先Webサーバーではなく、プロキシサーバー(おそらく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)間の接続は開いたままになります。プロキシサーバーは、P-Sとの間のC-P接続のすべてを中継します。クライアントは、そのチャネルでTLSハンドシェイクを開始することにより、アクティブ(P-S)接続をSSL/TLS接続にアップグレードします。これですべてがサーバーに中継されるため、TLS交換がwww.example.com:443で直接行われたかのようになります。

プロキシはハンドシェイクでは(したがってSNIでも)何の役割も果たしません。 TLSハンドシェイクは、クライアントとエンドサーバーの間で直接効果的に行われます。

プロキシサーバーを作成している場合、クライアントがHTTPSサーバーに接続できるようにするために必要なことはCONNECTリクエストで読み取られ、プロキシからエンドサーバーへの接続を確立します(CONNECT request)、クライアントに200 OK応答を送信し、クライアントから読み取ったすべてのものをサーバーに転送します(逆も同様)。

RFC 2616CONNECTを単純なトンネルを確立する方法として扱います(これがそのトンネルです)。詳細については RFC 2817 を参照してください。ただし、RFC 2817の残りの部分(非プロキシHTTP接続内でTLSにアップグレード)はほとんど使用されません。

クライアント(C)とプロキシ(P)の間でTLSを介して接続しようとしているようです。これは問題ありませんが、クライアントはCONNECTを使用して外部のWebサーバーに接続しません(HTTPSサーバーへの接続でない限り)。

35
Bruno

RFC 2616(セクション14.23)から:

Host request-headerフィールドは、リクエストされたリソースのインターネットホストとポート番号を指定します。これは、ユーザーまたは参照リソース(セクション3.2.2で説明されているように、通常はHTTP URL)によって与えられた元のURIから取得されます。 Hostフィールドの値は、元のURLで指定されたオリジンサーバーまたはゲートウェイの命名機関を表す必要があります。

私の理解では、CONNECT行からHost行にアドレスをコピーする必要があります。全体として、リソースのアドレスは192.168.1.1であり、example.com経由で接続しているという事実はRFCの観点からは何も変更しません。

あなたはすべてを正しく行っています。問題が発生したのはApacheです。 TLSを介したCONNECTのサポートは最近追加されただけで( https://issues.Apache.org/bugzilla/show_bug.cgi?id=29744 )、まだ解決されていないことがいくつかあります。あなたが直面している問題はそれらの1つです。

3
Nikratio

TLS(https)内にCONNECTメソッドが表示されることはほとんどありません。私は実際にそれを行うクライアントを知りません(そして、私はそれが誰であるかを知りたいので、それが実際に良い機能だと思います)。

通常、クライアントはhttp(プレーンtcp)を使用してプロキシに接続し、CONNECTメソッド(およびホストヘッダー)をHost:443に送信します。次に、プロキシはエンドポイントへの透過的な接続を行い、クライアントはSSLハンドシェイクを送信します。

このシナリオでは、データはsslで保護された「エンドツーエンド」です。

CONNECTメソッドは実際には指定されていません。HTTPRFCでのみ予約されています。ただし、通常は非常にシンプルであるため、相互運用が可能です。メソッドはHost [:port]を指定します。 Host:ヘッダーは単に無視できます。追加のプロキシ認証ヘッダーが必要になる場合があります。接続の本文が始まると、プロキシで解析を行う必要がなくなります(有効なSSLハンドシェイクをチェックするため、一部では解析が行われます)。

2
eckes