プロキシを経由してHTTPSのサイトを見るとき、PCとプロキシの間では CONNECT というリクエストが使われています。パケットキャプチャを取ると、最初に CONNECT www.example.com:443 が見え、そのあとはTLSの暗号化された通信が続きます。
ここでよくある誤解が「プロキシを通るHTTPSは、プロキシで一度復号されている」というものです。プロキシでTLSを復号する設定にしていなければ、TLSはPCとWebサーバの間で直接張られ、プロキシは暗号化されたデータを中継しているだけです。この記事では、CONNECTメソッドが何をしているのかを、TLS復号をしない場合とする場合のシーケンスで比べながら説明します。あわせて、プロキシ認証の407や、X-Forwarded-For・Forwarded・Via といったヘッダーとの関係も整理します。
- プロキシに送るリクエストの形(http:// と https:// の違い)
- CONNECTメソッドでトンネルを作る流れ
- TLS復号をしない場合とする場合で、何が違うのか
- 407とプロキシ認証(Proxy-Authenticate・Proxy-Authorization)
- X-Forwarded-For・Forwarded・Via は付くのか
この記事は、HTTPの仕様(RFC 9110、RFC 9112 など)と、Squid、Microsoft、Chromium などが公開しているドキュメントを参考に、プロキシに共通する考え方をまとめたものです。特定の製品の動作を説明・保証するものではありません。実際の動作は、お使いのプロキシ製品のドキュメントで確認してください。
プロキシに送るリクエストは3つの形
HTTPのリクエストの1行目は、送る相手によって書き方が変わります。Webサーバに直接送るときはパスだけを書きますが、プロキシに送るときは、プロキシが「どこへ取りに行けばよいか」が分かるように書く必要があります。
| 場面 | 1行目の形 | プロキシに分かること |
|---|---|---|
| 直接アクセス | パスだけ(GET /index.html) | (プロキシを通らない) |
| プロキシ経由で http:// | URLを丸ごと(GET http://www.example.com/index.html) | URL、ヘッダー、中身のすべて |
| プロキシ経由で https:// | ホスト名とポートだけ(CONNECT www.example.com:443) | つなぐ先のホスト名とポートだけ |
http:// のサイトなら、プロキシはリクエストの中身をすべて読めるので、代わりにWebサーバへ取りに行き、結果を返せます。https:// のサイトは中身が暗号化されているので、プロキシは代わりに取りに行けません。そこでPCは、プロキシに「この相手とつながる通り道(トンネル)だけ作ってほしい」と頼みます。これがCONNECTメソッドです。
CONNECTメソッドとは
CONNECTは、プロキシに「指定したホストのポートまで、TCPのトンネルを作ってほしい」と頼むメソッドです。プロキシは宛先に接続し、成功すれば 200 番台の応答を返します。そのあとは、PCとWebサーバの間を流れるデータを、中身を見ずにそのまま両方向へ中継します。
図2のとおり、TCPの接続はPCとプロキシ、プロキシとWebサーバの2本に分かれています。一方、TLSの暗号化はPCとWebサーバの間で1本だけです。プロキシは2本のTCPの間でバイト列を受け渡しているだけで、暗号化されたデータの中身は見ていません。
CONNECTのリクエストと応答の例
# PC → プロキシ CONNECT www.example.com:443 HTTP/1.1 Host: www.example.com:443 # プロキシ → PC HTTP/1.1 200 Connection established # ここから先はトンネル(TLSのClientHelloが流れる)
CONNECTの決まりごと
| 項目 | 内容 |
|---|---|
| 宛先の書き方 | 「ホスト名:ポート」だけを書く。ポート番号は省略できない |
| リクエストの中身 | CONNECTのリクエストには本文がない |
| 成功の応答 | 200番台が返ると、応答のヘッダーの直後からトンネルになる。「Connection established」という文言は慣習で、決まっているのは番号だけ |
| 失敗の応答 | 200番台以外が返ったときは、トンネルはできていない |
| キャッシュ | CONNECTの応答はキャッシュされない |
| 宛先ポートの制限 | どのポートにもつなげると、メール送信(25番)の踏み台などに悪用されるため、プロキシは接続できるポートを絞ることが求められている |
シーケンスで見る:TLS復号をしない場合
プロキシでTLSを復号しない場合の流れです。CONNECTと200のやり取りのあとは、プロキシは中継するだけになります。
ポイントは、TLSのハンドシェイク(⑤⑥)がPCとWebサーバの間で行われていることです。PCが受け取るのはWebサーバの本物の証明書で、暗号の鍵もPCとWebサーバだけが持っています。GET /index.html のような実際のリクエスト(⑦)は暗号化されているので、プロキシには読めません。また、名前解決はプロキシが行うので、PCは宛先のIPアドレスを知らなくてもかまいません。
プロキシに見えるもの・見えないもの
| 情報 | プロキシに見えるか | 理由 |
|---|---|---|
| 宛先のホスト名とポート | 見える | CONNECTの1行目に書かれている |
| SNI(TLSで名乗るホスト名) | 見える | ClientHelloの中で暗号化されずに送られる。ただし、ECH(Encrypted Client Hello)を使うと隠れる |
| サーバの証明書 | TLS 1.2 では見える、TLS 1.3 では見えない | TLS 1.3 では証明書のやり取りも暗号化される |
| URLのパスやクエリ | 見えない | 暗号化されたリクエストの中にある |
| ヘッダー・Cookie・本文 | 見えない | 同上 |
| 通信量・時間 | 見える | 中継しているので、量と時間は分かる |
URLフィルタがHTTPSのサイトを「ホスト名単位」でしか判断できないのはこのためです。パスまで見て判断したい、ファイルの中身を検査したい場合は、次のTLS復号が必要になります。
シーケンスで見る:TLS復号をする場合
プロキシでTLSを復号する(SSLインスペクション)場合も、PCが最初に送るのは同じCONNECTです。違いは200を返したあとで、プロキシがPCのTLSを自分で受け止め、Webサーバとは別にTLSを張ります。
プロキシは、Webサーバの本物の証明書をもとに、会社の認証局(CA)で署名した証明書をその場で作り、PCに渡します。PCは会社のCAを信頼するように設定されているので、警告は出ません。PCのブラウザで証明書を確認すると、発行者が会社のCAになっているのが目印です。
こうしてPCとプロキシの間(TLS①)、プロキシとWebサーバの間(TLS②)の2本のTLSになり、プロキシの中では通信が一度復号されます。URLのパスやファイルの中身まで検査できるのはこのためです。一方、証明書を固定している(ピンニングしている)アプリは、会社のCAの証明書を受け付けないので通信できなくなります。こうしたアプリは、復号の対象から外しておきます。
同じ「プロキシ経由のHTTPS」でも、復号するかどうかはプロキシの設定で決まり、宛先ごとに変えられます。たとえば、金融機関や医療系のサイトは復号せずにトンネルのまま通す、という設定がよく使われます。
プロキシ認証と407
プロキシが「誰が使っているのか」を確かめる場合、最初のCONNECTに対して 407 Proxy Authentication Required を返します。PCは認証の情報を付けて、もう一度CONNECTを送ります。
| ヘッダー・番号 | 向き | 意味 |
|---|---|---|
| 407 | プロキシ → PC | プロキシを使うには認証が必要、という応答 |
| Proxy-Authenticate | プロキシ → PC | 407に付けて、使える認証方式(Basic、Negotiate など)を伝える |
| Proxy-Authorization | PC → プロキシ | 認証の情報を載せる。受け取ったプロキシが使い終わり、Webサーバには送られない |
Webサーバ自身が認証を求めるときは 401・WWW-Authenticate・Authorization を使います。407の仲間は「プロキシ用」で、すぐ隣のプロキシとの間だけで使われる、と覚えると区別しやすくなります。
Basic認証の例
# プロキシ → PC HTTP/1.1 407 Proxy Authentication Required Proxy-Authenticate: Basic realm="corp-proxy" # PC → プロキシ(ユーザー名:パスワードをBase64にしたもの) CONNECT www.example.com:443 HTTP/1.1 Host: www.example.com:443 Proxy-Authorization: Basic dXNlcjE6cGFzc3dvcmQ=
Basic認証のBase64は暗号ではなく、誰でも元に戻せます。PCとプロキシの間が暗号化されていなければ、パスワードがネットワークに流れることになります。企業では、Active Directoryと連携したNegotiate(Kerberos)やNTLMがよく使われます。
NTLM・Negotiateでは407が何回か返る
NTLMやNegotiateは、同じTCP接続の上で何往復かやり取りして認証します。そのため、パケットキャプチャで407が2回続いても、異常ではありません。
途中でTCP接続が切れると、認証を最初からやり直すことになります。プロキシや途中の機器がTCP接続を維持(キープアライブ)しないと、認証が終わらずに407が繰り返されることがあります。
X-Forwarded-For・Forwarded・Via はどうなるのか
プロキシを通ると、Webサーバから見た送信元のIPアドレスはプロキシになります。そこで、プロキシが元の送信元などをヘッダーに書き足す仕組みがあります。
| ヘッダー | 内容 | 位置づけ |
|---|---|---|
| Via | 経由したプロキシ(プロトコルのバージョンと名前) | HTTPの仕様で、プロキシは付けることになっている |
| X-Forwarded-For | 元の送信元のIPアドレス | 仕様にはないが、広く使われている |
| Forwarded | 元の送信元・プロトコル・経由したプロキシなど | RFC 7239 で標準化された、X-Forwarded-For の後継 |
大事なのは、これらのヘッダーを書き足せるのは、プロキシがHTTPのリクエストを読める場合だけだということです。CONNECTのトンネルの中を流れるHTTPSのリクエストは暗号化されているので、TLS復号をしないプロキシは、中のヘッダーを読むことも書き足すこともできません。
図7では、社員のPCのIPアドレスを 10.0.10.25、プロキシの名前を proxy1(IPアドレスは 10.0.0.8)としています。Via: 1.1 proxy1 は「HTTP/1.1 で受け取り、proxy1 というプロキシを経由した」という意味で、X-Forwarded-For: 10.0.10.25 は元の送信元であるPCのIPアドレスです。Webサーバから見た通信の送信元は、どの場合もプロキシの 10.0.0.8 です。
| 場面 | ヘッダーを付けられるか | Webサーバから見えるもの |
|---|---|---|
| http://(暗号化なし) | 付けられる | 送信元IPはプロキシ。X-Forwarded-For があれば元のIPも分かる |
| https://・TLS復号なし | 付けられない | 送信元IPはプロキシだけ。元のIPは分からない |
| https://・TLS復号あり | 設定しだいで付けられる | http:// と同じ |
X-Forwarded-For には社内のIPアドレスが入るので、社外のサイトに社内のアドレスを知らせたくない場合は、付けない設定や消す設定にすることもあります。また、Webサーバの側では、X-Forwarded-For は利用者が自分で書き換えられることを前提に、信頼できるプロキシから来た値だけを使います。
そのほか知っておくと役立つこと
接続できるポートの制限
多くのプロキシは、CONNECTで接続できる宛先のポートを、既定で443番などに絞っています。443番以外のポートを使うHTTPSのサイトや、プロキシ経由でメールを送ろうとしたときにつながらない場合は、この制限が原因のことがあります。その場合、プロキシは403などのエラーを返します。
プロキシが返す主なエラー
| 番号 | よくある原因 |
|---|---|
| 403 | プロキシのルールで禁止されている宛先やポート |
| 407 | プロキシの認証が必要、または認証に失敗した |
| 502・503・504 | プロキシから宛先のサーバにつながらない、応答がない(どの番号になるかは製品による) |
WebSocket・HTTP/2・HTTP/3
| 項目 | 内容 |
|---|---|
| WebSocket | ブラウザがプロキシ経由で WebSocket(ws:// や wss://)につなぐときも、CONNECTでトンネルを作る |
| HTTP/2 のCONNECT | HTTP/2 では、1本の接続の中の1つのストリームが1本のトンネルになる |
| HTTP/3(QUIC) | QUICはUDPを使うので、通常のCONNECT(TCPのトンネル)には乗らない。プロキシを設定したブラウザは、TCPのHTTP/2やHTTP/1.1でつなぐ。UDPを中継するための CONNECT-UDP(RFC 9298)という仕組みもある |
| HTTPSプロキシ | PCとプロキシの間もTLSで暗号化する方式。CONNECTの宛先やプロキシ認証の情報も隠せる。中のトンネルには、さらにPCとWebサーバの間のTLSが通る |
よくある誤解
| 誤解 | 実際 |
|---|---|
| プロキシを通るHTTPSは、プロキシで復号されている | TLS復号の設定をしていなければ、TLSはPCとWebサーバの間で張られ、プロキシは中継するだけ |
| CONNECTのあと、プロキシがWebサーバとTLSを張る | TLS復号なしなら、TLSのハンドシェイクをするのはPC自身。プロキシが張るのはTCPだけ |
| プロキシならHTTPSでもURLが全部見える | TLS復号なしで見えるのは、ホスト名とポートまで |
| X-Forwarded-For はプロキシが必ず付ける | TLS復号なしのHTTPSでは、トンネルの中のリクエストに付けられない |
| 407はWebサーバのエラー | プロキシからの「認証してください」という応答 |
| 407が何度も返るのは失敗している | NTLM・Negotiateでは、何往復かするのが正常 |
まとめ
CONNECTメソッドは、プロキシに「この相手までトンネルを作ってほしい」と頼むためのメソッドです。トンネルができたあと、TLSはPCとWebサーバの間で張られ、プロキシは中継するだけになります。TLS復号をする場合だけ、プロキシの中でTLSが2本に分かれ、中身を検査できるようになります。
| TLS復号なし | TLS復号あり | |
|---|---|---|
| 最初のリクエスト | CONNECT ホスト名:443 | CONNECT ホスト名:443 |
| TLSの本数 | 1本(PC ⇔ Webサーバ) | 2本(PC ⇔ プロキシ、プロキシ ⇔ Webサーバ) |
| PCが受け取る証明書 | Webサーバの本物の証明書 | 会社のCAで署名した証明書 |
| プロキシに見えるもの | ホスト名・ポート・SNI・通信量 | URL・ヘッダー・中身まで |
| X-Forwarded-For・Via | 付けられない | 設定しだいで付けられる |
| プロキシ認証(407) | どちらもCONNECTの段階で行う。Proxy-Authorization はプロキシで使い終わる | |
コメント