https_proxy=httpとhttps_proxy=httpsは何が違うのか？


# 1. はじめに

ことの発端は、[IBM Cloudの CLIでProxyを設定できるかどうかを検証](https://qiita.com/testnin2/items/7d05acd6ab6d36fc91a0)していた時である。
その記事では、

- `export https_proxy=http://10.0.0.4:3128`

としたが、

- `export https_proxy=https://10.0.0.4:3128`

ではなぜダメなのか？と思った次第。実際、後者のやり方で実行すると、IBM Cloud CLIで実行すると`proxyconnect tcp: tls: first record does not look like a TLS handshake`というエラーが出たので、やはり挙動としては違いはあるはずである。そもそも設定上の組み合わせとしては以下の4つがあるはず。Case1はhttpでの指定として自然だし、Case3はhttpへのアクセスにhttpsを利用するという上であり得ない組み合わせのはずであり、やはりキモとなるのはCase2とCase4の違いである。

```
Case1: http_proxy = http://proxy:port
Case2: https_proxy = http://proxy:port
Case3: http_proxy = https://proxy:port
Case4: https_proxy = https://proxy:port
```

# 2. 結論

以下のdiscussionや記述が役に立った。
- https://stackoverflow.com/questions/58559109/difference-between-http-proxy-and-https-proxy
- https://wiki.squid-cache.org/Features/HTTPS#CONNECT_tunnel
- https://nw.seeeko.com/archives/50540600.html


Case2の時は、いわゆる多くの記事で紹介されているPROXY越えのHTTPS通信の話であり、例えば[この記事](https://milestone-of-se.nesuke.com/nw-basic/grasp-nw/proxy/)の解説などが詳しくてわかりやすい。

- CONNECTメソッドによってPROXYにTLSセッションを中継するように指示する。PROXYは、PROXYとサーバー間で**3 way handshakeを完了してTCPレベルでセッションが張れて中継できることを確認した上で**、クライアントに`Connection established`を返す。
- 一旦、PROXYが中継を行うと、TLSセッションはクライアントとサーバーの間で張られる。PROXYはTLSやhttps通信の中身には一切触れず（触れることはできず）、IPヘッダとTCPヘッダを変更してサーバーに転送するだけである。

Case4の時は、

- クライアントとPROXY間でTLSセッションを張り、なおかつPROXYとサーバー間でもTLSセッションを張る。
- クライアントからCONNECTメソッドによるPROXYを中継する指示が来た際には、PROXYは、PROXYとサーバー間で**3 way handshakeだけでなくTLSセッションが張れて中継できることを確認した上で**、クライアントに`Connection established`を返す。
- クライアントからPROXYにTLSセッションを張る際には、サーバーのFQDNに対してアクセスを要求してPROXYが証明書を返すのだから、当然PROXYサーバー側でその対応が必要となる。例えば、1) PROXY側でアクセス先のサーバーのFQDNに対して署名を行い（一般的には、都度署名を行う仕組みを設ける）、２）クライアントでそのPROXYサーバーが署名した証明書を信頼できるように、クライアントにPROXYサーバで署名を行うCAのCA証明書を導入する必要がある。
- クライアントはサーバーが提供する証明書ではなく、PROXYサーバーが署名した証明書情報しか確認できないため、中間者攻撃が行われるかどうかの確認ができないことに注意。
- ネットワークスペシャリスト 平成26年秋期試験問題 午後Ⅱより引用
    - https://www.nw-siken.com/kakomon/26_aki/ <br> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/88a757c8-5dbb-ff5b-1f08-2de5af7d7690.png)

「はじめに」で書いた、IBM Cloud CLIのエラーも、PROXYと間でTLSセッションを張ろうとしてエラーになったのだろうと想像される（エラーメッセージもそのような内容である）。

# 3. curl version 7.29.0利用時の比較

これらの環境変数をどのようにハンドリングするかは、厳密にはアプリには依存する。
実際、OS付属のcurlだとCase2/Case4どちらの場合でも挙動は同じだった。このcurlのバージョンではPROXYにHTTPS接続するという仕組みが実装されていなかったのだと思われる。

```txt:Case2 + curl 7.29.0
[root@syasuda-metavsi ~]# curl --version
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.53.1 zlib/1.2.7 libidn/1.28 libssh2/1.8.0
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-sockets
[root@syasuda-metavsi ~]# export https_proxy=http://10.0.0.4:3128
[root@syasuda-metavsi ~]# curl -v -I https://www.yahoo.co.jp
* About to connect() to proxy 10.0.0.4 port 3128 (#0)
*   Trying 10.0.0.4...
* Connected to 10.0.0.4 (10.0.0.4) port 3128 (#0)
* Establish HTTP proxy tunnel to www.yahoo.co.jp:443
> CONNECT www.yahoo.co.jp:443 HTTP/1.1
> Host: www.yahoo.co.jp:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
HTTP/1.1 200 Connection established
<

* Proxy replied OK to CONNECT request
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* 	subject: CN=edge01.yahoo.co.jp,O=Yahoo Japan Corporation,L=Chiyoda-ku,ST=Tokyo,C=JP
* 	start date:  4月 06 10:01:27 2022 GMT
* 	expire date:  5月 05 14:59:00 2023 GMT
* 	common name: edge01.yahoo.co.jp
* 	issuer: CN=Cybertrust Japan SureServer CA G4,O="Cybertrust Japan Co., Ltd.",C=JP
> HEAD / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.yahoo.co.jp
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-Length: 0
Content-Length: 0
< Content-Type: text/html; charset=utf-8
Content-Type: text/html; charset=utf-8
< Date: Tue, 17 May 2022 02:23:21 GMT
Date: Tue, 17 May 2022 02:23:21 GMT
< Etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
Etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
< Vary: Accept-Encoding
Vary: Accept-Encoding
< X-Vcap-Request-Id: 41059c61-6a1b-4759-4983-4b03744d5075
X-Vcap-Request-Id: 41059c61-6a1b-4759-4983-4b03744d5075
< X-Xss-Protection: 1; mode=block
X-Xss-Protection: 1; mode=block
< Age: 0
Age: 0
< Connection: keep-alive
Connection: keep-alive
< Server: ATS
Server: ATS

<
* Connection #0 to host 10.0.0.4 left intact
```

```txt:Case4 + curl 7.29.0
[root@syasuda-metavsi ~]# curl --version
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.53.1 zlib/1.2.7 libidn/1.28 libssh2/1.8.0
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-sockets
[root@syasuda-metavsi ~]# export https_proxy=https://10.0.0.4:3128
[root@syasuda-metavsi ~]# curl -v -I https://www.yahoo.co.jp
* About to connect() to proxy 10.0.0.4 port 3128 (#0)
*   Trying 10.0.0.4...
* Connected to 10.0.0.4 (10.0.0.4) port 3128 (#0)
* Establish HTTP proxy tunnel to www.yahoo.co.jp:443
> CONNECT www.yahoo.co.jp:443 HTTP/1.1
> Host: www.yahoo.co.jp:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
HTTP/1.1 200 Connection established
<

* Proxy replied OK to CONNECT request
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* 	subject: CN=edge01.yahoo.co.jp,O=Yahoo Japan Corporation,L=Chiyoda-ku,ST=Tokyo,C=JP
* 	start date:  4月 06 10:01:27 2022 GMT
* 	expire date:  5月 05 14:59:00 2023 GMT
* 	common name: edge01.yahoo.co.jp
* 	issuer: CN=Cybertrust Japan SureServer CA G4,O="Cybertrust Japan Co., Ltd.",C=JP
> HEAD / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.yahoo.co.jp
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-Length: 0
Content-Length: 0
< Content-Type: text/html; charset=utf-8
Content-Type: text/html; charset=utf-8
< Date: Tue, 17 May 2022 02:23:58 GMT
Date: Tue, 17 May 2022 02:23:58 GMT
< Etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
Etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
< Vary: Accept-Encoding
Vary: Accept-Encoding
< X-Vcap-Request-Id: 14a23e09-4528-4039-6c39-bee89791aea0
X-Vcap-Request-Id: 14a23e09-4528-4039-6c39-bee89791aea0
< X-Xss-Protection: 1; mode=block
X-Xss-Protection: 1; mode=block
< Age: 0
Age: 0
< Connection: keep-alive
Connection: keep-alive
< Server: ATS
Server: ATS

<
* Connection #0 to host 10.0.0.4 left intact
```



# 4. curl version 7.83.1利用時の比較


- HTTP2に関する機能（抜粋）
    - https://curl.se/docs/http2.html
    - 7.43.0で[HTTP/2]の多重化に完全対応。
    - 7.47.0で[curl]コマンドは[HTTPS]ならデフォルトで[HTTP/2]で接続するようになった。

- SSL Certificate Verificationに関する機能（抜粋）
    - https://curl.se/docs/sslcerts.html
    - **Curl 7.52からHTTPSでのPROXYへの接続をサポートするようになった**
        
> Since version 7.52.0, curl can do HTTPS to the proxy separately from the connection to the server. This TLS connection is handled separately from the server connection so instead of --insecure and --cacert to control the certificate verification, you use --proxy-insecure and --proxy-cacert. With these options, you make sure that the TLS connection and the trust of the proxy can be kept totally separate from the TLS connection to the server.


```txt:curl7.83.1の導入
https://hanamiche.hatenablog.jp/entry/2021/04/10/073000
https://github.com/moparisthebest/static-curl/releases/tag/v7.83.1

# curl -L -O https://github.com/moparisthebest/static-curl/releases/download/v7.83.1/curl-amd64
# ln -s /etc/ssl/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt
# chmod +x curl-amd64
# ./curl-amd64 --version
curl 7.83.1 (x86_64-pc-linux-musl) libcurl/7.83.1 OpenSSL/1.1.1o zlib/1.2.12 libssh2/1.10.0 nghttp2/1.46.0
Release-Date: 2022-05-11
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HSTS HTTP2 HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL TLS-SRP UnixSockets
```


```txt:Case2 + curl 7.83.1
[root@syasuda-metavsi ~]# export https_proxy=http://10.0.0.4:3128
[root@syasuda-metavsi ~]# ./curl-amd64 -v -I https://www.yahoo.co.jp
* Uses proxy env variable https_proxy == 'http://10.0.0.4:3128'
*   Trying 10.0.0.4:3128...
* Connected to 10.0.0.4 (10.0.0.4) port 3128 (#0)
* allocate connect buffer
* Establish HTTP proxy tunnel to www.yahoo.co.jp:443
> CONNECT www.yahoo.co.jp:443 HTTP/1.1
> Host: www.yahoo.co.jp:443
> User-Agent: curl/7.83.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
HTTP/1.1 200 Connection established
<

* Proxy replied 200 to CONNECT request
* CONNECT phase completed
* ALPN: offers h2
* ALPN: offers http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: C=JP; ST=Tokyo; L=Chiyoda-ku; O=Yahoo Japan Corporation; CN=edge01.yahoo.co.jp
*  start date: Apr  6 10:01:27 2022 GMT
*  expire date: May  5 14:59:00 2023 GMT
*  subjectAltName: host "www.yahoo.co.jp" matched cert's "*.yahoo.co.jp"
*  issuer: C=JP; O=Cybertrust Japan Co., Ltd.; CN=Cybertrust Japan SureServer CA G4
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* h2h3 [:method: HEAD]
* h2h3 [:path: /]
* h2h3 [:scheme: https]
* h2h3 [:authority: www.yahoo.co.jp]
* h2h3 [user-agent: curl/7.83.1]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0x7fe6eba971b0)
> HEAD / HTTP/2
> Host: www.yahoo.co.jp
> user-agent: curl/7.83.1
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200
HTTP/2 200
< server: ATS
server: ATS
< date: Tue, 17 May 2022 02:32:11 GMT
date: Tue, 17 May 2022 02:32:11 GMT
< content-type: text/html; charset=utf-8
content-type: text/html; charset=utf-8
< content-length: 0
content-length: 0
< etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
< vary: Accept-Encoding
vary: Accept-Encoding
< x-vcap-request-id: 9dc258de-ab84-4e68-47b5-c1e2494d46fb
x-vcap-request-id: 9dc258de-ab84-4e68-47b5-c1e2494d46fb
< x-xss-protection: 1; mode=block
x-xss-protection: 1; mode=block
< age: 0
age: 0

<
* Connection #0 to host 10.0.0.4 left intact
```

```txt:Case4 + curl 7.83.1
[root@syasuda-metavsi ~]# export https_proxy=https://10.0.0.4:3128
[root@syasuda-metavsi ~]# ./curl-amd64 -v -I https://www.yahoo.co.jp
* Uses proxy env variable https_proxy == 'https://10.0.0.4:3128'
*   Trying 10.0.0.4:3128...
* Connected to 10.0.0.4 (10.0.0.4) port 3128 (#0)
* ALPN: offers http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* error:1408F10B:SSL routines:ssl3_get_record:wrong version number
* Closing connection 0
curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number
```

少なくとも挙動が変わったことがわかる。