gokigenmaruのブログ

40から始めるクラウドエンジニア

curlのオプション"-k"について

とあるプロジェクトで独自のルート認証局を利用しサーバ証明書を作成(よくいうオレオレ証明書)し、https接続を実施している環境があり、その運用担当者から質問を受けました。
「自分のところは独自のルート認証局から発行しているサーバ証明書を使って接続させているにも関わらず、独自のルート証明書を持っていないであろう、FQDNではなくIPアドレスを直接指定してきているクライアントから接続を受けている。通常、ルート証明書を持っていない and IPアドレス直指定やこちらの証明書で設定しているCNとは異なるFQDNで接続してきた場合はSSLのエラーになって接続出来ないはずなのに、なんで接続が出来るのか?」

私はこう返答しました。
ルート証明書が異なっていたり、IPや異なるFQDNで接続するとエラーが返って接続出来ないと思っているでしょうが、そんなことはないですよ。curlとかで-kオプションってご存じです?証明書のエラーを無視して処理を進めるオプションです。そのようなものを使えば独自のルート証明書を持っていないクライアントやIPアドレスを直接FQDNとして叩いてきてもサーバに接続が出来ます。」

どうやら、独自のルート証明書を持っていないユーザは接続出来ないことでセキュリティを担保しようと思っていたようで、接続できると知った時にエーってなってました。
その時にふと思いました。そういえば、-kオプションつけたらなんで接続できるのかなと…。

ということで調べてみました。

curlのオプション"-k"とは

curlのmanで-kオプションのところを見てみました。

       -k, --insecure
              (TLS)  By default, every SSL connection curl makes is verified to be secure. This option allows curl to
              proceed and operate even for server connections otherwise considered insecure.

              The server connection is verified by making sure the server's certificate contains the right  name  and
              verifies successfully using the cert store.

              See this online resource for further details:
               https://curl.haxx.se/docs/sslcerts.html

              See also --proxy-insecure and --cacert.

ざっくり訳すと、
「デフォルトはSSL通信は安全だと接続出来ます。このオプションを使うと安全でないサーバでもcurlで接続できます。」
って感じでしょうか。
つまり、SSLのエラーを無視するという捉え方が正しいのかなぁと思いました。

実際にcurlで接続してみる

勝手に(無許可で)yahooさんにご協力いただき、curl接続をしてみました。
まずは普通の接続。

$ curl -vvv https://www.yahoo.co.jp/
*   Trying 183.79.250.123:443...
* TCP_NODELAY set
* Connected to www.yahoo.co.jp (183.79.250.123) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* 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 to use h2
* Server certificate:
*  subject: C=JP; ST=Tokyo; L=Chiyoda-ku; O=Yahoo Japan Corporation; CN=edge01.yahoo.co.jp
*  start date: Jan 27 04:01:20 2022 GMT
*  expire date: Feb 26 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 multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x557efce81880)
> GET / HTTP/2
> Host: www.yahoo.co.jp
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
以下省略…

SAN(subjectAltName)のチェックで「host "www.yahoo.co.jp" matched cert's "*.yahoo.co.jp"」でFQDNと証明書のSANがmatchedしていますね。
yahooさんはワイルドカード証明書を利用していることもここで分かります。「"*.yahoo.co.jp"」ですね。
そして、issuerも問題なし。(証明書は「Cybertrust Japan」が発行しているようですね、Cybertrustのルート証明書はPCにデフォで入っているので当然問題なしです)
ということで「SSL certificate verify ok」が出ています。これは当然ですね。

ここでcurlのオプションを使ってFQDNを「www.google.co.jp」に変えてwww.yahoo.co.jpに接続してみます。
yahooさんのIPは事前にdigで確認済みです。

$ curl -vvv --resolve www.google.co.jp:443:183.79.250.251 https://www.google.co.jp/
* Added www.google.co.jp:443:183.79.250.251 to DNS cache
* Hostname www.google.co.jp was found in DNS cache
*   Trying 183.79.250.251:443...
* TCP_NODELAY set
* Connected to www.google.co.jp (183.79.250.251) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* 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 to use h2
* Server certificate:
*  subject: C=JP; ST=Tokyo; L=Chiyoda-ku; O=Yahoo Japan Corporation; CN=edge01.yahoo.co.jp
*  start date: Jan 27 04:01:20 2022 GMT
*  expire date: Feb 26 14:59:00 2023 GMT
*  subjectAltName does not match www.google.co.jp
* SSL: no alternative certificate subject name matches target host name 'www.google.co.jp'
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, close notify (256):
curl: (60) SSL: no alternative certificate subject name matches target host name 'www.google.co.jp'
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

エラーになって接続が出来ませんでした。
エラーの内容は「no alternative certificate subject name matches target host name」で、ターゲットのhost nameと証明書のsubject nameが違うよと怒られています。
SAN(subjectAltName)のチェックで「subjectAltName does not match www.google.co.jp」と出ています。証明書のSANと接続の際に利用したFQDNである「www.google.co.jp」が違うと言われています。
1個前のcurlでyahooさんが利用しているのは"*.yahoo.co.jp"の証明書ですので、www.google.co.jpでは証明書でエラーが出るのは当然です。

ここで-kオプションを使ってみます。

$ curl -vvv -k --resolve www.google.co.jp:443:183.79.250.251 https://www.google.co.jp/
* Added www.google.co.jp:443:183.79.250.251 to DNS cache
* Hostname www.google.co.jp was found in DNS cache
*   Trying 183.79.250.251:443...
* TCP_NODELAY set
* Connected to www.google.co.jp (183.79.250.251) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* 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 to use h2
* Server certificate:
*  subject: C=JP; ST=Tokyo; L=Chiyoda-ku; O=Yahoo Japan Corporation; CN=edge01.yahoo.co.jp
*  start date: Jan 27 04:01:20 2022 GMT
*  expire date: Feb 26 14:59:00 2023 GMT
*  issuer: C=JP; O=Cybertrust Japan Co., Ltd.; CN=Cybertrust Japan SureServer CA G4
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x562cecdc9880)
> GET / HTTP/2
> Host: www.google.co.jp
> user-agent: curl/7.68.0
> accept: */*
以下略…

接続出来ました。
先ほどエラーとなっていたところのSAN(subjectAltName)のチェックがありません。SANのチェックが無いので証明書のCNとFQDNが違くても(そもそもチェックをしていない)接続が出来ています。
つまり、当初思っていたエラーを無視するのではなく、そもそもSANのチェックをしないが正しいんですかね。

とここでふと思いました。
curlでリクエストヘッダを今回接続するyahooさんではなく、「www.google.co.jp」を指定するとどうなるのか。
ということで試してみました。

$ curl -vvv --header 'Host:www.google.co.jp' https://www.yahoo.co.jp/
*   Trying 182.22.28.252:443...
* TCP_NODELAY set
* Connected to www.yahoo.co.jp (182.22.28.252) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* 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 to use h2
* Server certificate:
*  subject: C=JP; ST=Tokyo; L=Chiyoda-ku; O=Yahoo Japan Corporation; CN=edge01.yahoo.co.jp
*  start date: Jan 27 04:01:20 2022 GMT
*  expire date: Feb 26 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 multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x556bfe343880)
> GET / HTTP/2
> Host:www.google.co.jp
> user-agent: curl/7.68.0
> accept: */*
以下略…

通りましたね。まぁなんとなく気づいてました(汗)
SSL(TLS)の場合、接続先のFQDNはClientHelloの際にSNI(Server Name Identification)値を見ている(と思っています)ので、HTTPのリクエストヘッダを変えてもダメですね。





とここで長くなってしまったので、続きはまた後日に書こうと思います。