環境
事象
とあるダイナミックDNSサービスを長年愛用している。
自宅のIPアドレスが変更されたらcurlコマンドを叩き、そのダイナミックDNSサービスに通知する仕組みにしていた。
ところが、Ubuntu 18.04に上げてから error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure でcurlコマンドが失敗するようになった。
$ curl -v -s -o /dev/null https://XXXXXX.net/
* Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) 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
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [210 bytes data]
* TLSv1.2 (IN), TLS alert, Server hello (2):
{ [2 bytes data]
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* stopped the pause stream!
* Closing connection 0
curlとOpenSSLのバージョン情報
$ curl --version curl 7.58.0 (arm-unknown-linux-gnueabihf) libcurl/7.58.0 OpenSSL/1.1.0g zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3 Release-Date: 2018-01-24 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL $ openssl version OpenSSL 1.1.0g 2 Nov 2017
原因
Webサーバ側が対応しているCipher Suiteが古くてセキュアではないものだった。 Ubuntu 18.04のcurl/OpenSSLでは、それらのCipher Suiteが非対応になっていた。
解決策
Webクライアント側でできることは何もない。 Ubuntu 18.04のcurl/OpenSSLで利用可能なCipher Suiteの中に、目的のWebサーバが対応しているものはなかったから。 SSLv3や脆弱な暗号化方式を有効にしてOpenSSLをコンパイルし直す手もあるが、セキュリティ的に良くないし、そこまで頑張る必要性もない。
だから、別のダイナミックDNSサービスに乗り換えよう…
原因究明の過程
いろいろなOSで試してみた
次の3通りのOS/バージョンで試してみたところ、すべて接続可能だった。
- Ubuntu 16.04.5 / curl 7.47.0 / OpenSSL 1.0.2g
SSL connection using TLS1.0 / RSA_3DES_EDE_CBC_SHA1
- macOS Sierra 10.12.6 / curl 7.54.0 / OpenSSL 0.9.8zh
TLS 1.0 connection using TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
- macOS Mojave 10.14.4 / curl 7.54.0 / LibreSSL 2.6.5
SSL connection using TLSv1.0 / EDH-RSA-DES-CBC3-SHA
Ubuntu 16.04.5 / curl 7.47.0 / OpenSSL 1.0.2g
$ curl -v -s -o /dev/null https://XXXXXX.net/
* Trying XX.XX.XX.XX...
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* found 148 certificates in /etc/ssl/certs/ca-certificates.crt
* found 592 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.0 / RSA_3DES_EDE_CBC_SHA1
* server certificate verification OK
* server certificate status verification SKIPPED
* common name: XXXXXX.net (matched)
* server certificate expiration date OK
* server certificate activation date OK
* certificate public key: RSA
* certificate version: #3
* subject: CN=XXXXXX.net
* start date: Fri, 21 Dec 2018 00:00:00 GMT
* expire date: Thu, 18 Feb 2021 12:00:00 GMT
* issuer: C=US,O=DigiCert Inc,OU=www.digicert.com,CN=RapidSSL RSA CA 2018
* compression: NULL
* ALPN, server did not agree to a protocol
> GET / HTTP/1.1
> Host: XXXXXX.net
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 03 May 2019 06:15:29 GMT
< Server: Apache
< Transfer-Encoding: chunked
< Content-Type: text/html
<
{ [6 bytes data]
* Connection #0 to host XXXXXX.net left intact
$ curl --version
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets
$ openssl version
OpenSSL 1.0.2g 1 Mar 2016
macOS Sierra 10.12.6 / curl 7.54.0 / OpenSSL 0.9.8zh
$ curl -v -s -o /dev/null https://XXXXXX.net
* Rebuilt URL to: https://XXXXXX.net/
* Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* TLS 1.0 connection using TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
* Server certificate: XXXXXX.net
* Server certificate: RapidSSL RSA CA 2018
* Server certificate: DigiCert Global Root CA
> GET / HTTP/1.1
> Host: XXXXXX.net
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 03 May 2019 05:40:26 GMT
< Server: Apache
< Transfer-Encoding: chunked
< Content-Type: text/html
<
{ [6 bytes data]
* Connection #0 to host XXXXXX.net left intact
$ curl --version
curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets
$ openssl version
OpenSSL 0.9.8zh 14 Jan 2016
macOS Mojave 10.14.4 / curl 7.54.0 / LibreSSL 2.6.5
$ curl -v -s -o /dev/null https://XXXXXX.net/
* Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [218 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [74 bytes data]
* TLSv1.0 (IN), TLS handshake, Certificate (11):
{ [3917 bytes data]
* TLSv1.0 (IN), TLS handshake, Server key exchange (12):
{ [525 bytes data]
* TLSv1.0 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.0 (OUT), TLS handshake, Client key exchange (16):
} [134 bytes data]
* TLSv1.0 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.0 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.0 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.0 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.0 / EDH-RSA-DES-CBC3-SHA
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=XXXXXX.net
* start date: Dec 21 00:00:00 2018 GMT
* expire date: Feb 18 12:00:00 2021 GMT
* subjectAltName: host "XXXXXX.net" matched cert's "XXXXXX.net"
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=RapidSSL RSA CA 2018
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: XXXXXX.net
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 03 May 2019 05:41:36 GMT
< Server: Apache
< Transfer-Encoding: chunked
< Content-Type: text/html
<
{ [6 bytes data]
* Connection #0 to host XXXXXX.net left intact
$ curl --version
curl 7.54.0 (x86_64-apple-darwin18.0) libcurl/7.54.0 LibreSSL/2.6.5 zlib/1.2.11 nghttp2/1.24.1
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets HTTPS-proxy
$ openssl version
LibreSSL 2.6.5
SSL/TLSバージョンを指定してみた
TLS 1.0
オプションを付けてTLS 1.0を強制してみたが、結果は変わらず error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure が出てしまった。
$ curl --tlsv1.0 -v -s -o /dev/null https://XXXXXX.net/
* Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) 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
} [5 bytes data]
* TLSv1.0 (OUT), TLS handshake, Client hello (1):
} [136 bytes data]
* TLSv1.0 (IN), TLS alert, Server hello (2):
{ [2 bytes data]
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* stopped the pause stream!
* Closing connection 0
SSLv3
$ curl --sslv3 -v -s -o /dev/null https://XXXXXX.net/ * Trying XX.XX.XX.XX... * TCP_NODELAY set * Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0) * OpenSSL was built without SSLv3 support * Closing connection 0
パケットキャプチャを見てみた
Ubuntu 18.04.2 / curl 7.58.0 / OpenSSL 1.1.0g
特にオプションを付けずに叩いたとき curl -v -s -o /dev/null https://XXXXXX.net
Client Helloの後、Webサーバから Handshake Failure のAlertが返ってきていた。

Client HelloではTLS 1.2と28個のCipher Suiteを提示していた。

TLSv1.0を強制して叩いたとき curl --tlsv1.0 -v -s -o /dev/null https://XXXXXX.net/
Client Helloの後、Webサーバから Handshake Failure のAlertが返ってきたのは同じ。
Client HelloではTLS 1.0と9個のCipher Suiteを提示していた。

Ubuntu 16.04.5 / curl 7.47.0 / OpenSSL 1.0.2g
特にオプションを付けずに curl -v -s -o /dev/null https://XXXXXX.net を実行したときのパケットキャプチャを見る。
Client Helloの後、ちゃんとServer Helloが返ってきていた。

Client HelloではTLS 1.2と54個のCipher Suiteを提示していた。Ubuntu 18.04よりも多い!

Server HelloではTLS 1.0とCipher Suite TLS_RSA_WITH_3DES_EDE_CBC_SHA が指定されていた。

OpenSSL 1.1.0g が対応しているCipher Suiteの一覧
Ubuntu 18.04.2のOpenSSL 1.1.0gが対応しているCipher Suiteは全部で57個ある。 Ubuntu 16.04.5のOpenSSL 1.0.2gでは97個だったので、だいぶ減った模様。
Ubuntu 16.04.5とmacOS Sierraで選択された SRP-RSA-3DES-EDE-CBC-SHA と、 macOS Mojaveで選択された EDH-RSA-DES-CBC3-SHA はこのリストの中にはない。
$ openssl ciphers -v ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(256) Mac=AEAD ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH Au=RSA Enc=CHACHA20/POLY1305(256) Mac=AEAD DHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=DH Au=RSA Enc=CHACHA20/POLY1305(256) Mac=AEAD ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(128) Mac=AEAD ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384 ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384 DHE-RSA-AES256-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(256) Mac=SHA256 ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256 ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256 DHE-RSA-AES128-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(128) Mac=SHA256 ECDHE-ECDSA-AES256-SHA TLSv1 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1 ECDHE-RSA-AES256-SHA TLSv1 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1 DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 ECDHE-ECDSA-AES128-SHA TLSv1 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1 ECDHE-RSA-AES128-SHA TLSv1 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1 DHE-RSA-AES128-SHA SSLv3 Kx=DH Au=RSA Enc=AES(128) Mac=SHA1 RSA-PSK-AES256-GCM-SHA384 TLSv1.2 Kx=RSAPSK Au=RSA Enc=AESGCM(256) Mac=AEAD DHE-PSK-AES256-GCM-SHA384 TLSv1.2 Kx=DHEPSK Au=PSK Enc=AESGCM(256) Mac=AEAD RSA-PSK-CHACHA20-POLY1305 TLSv1.2 Kx=RSAPSK Au=RSA Enc=CHACHA20/POLY1305(256) Mac=AEAD DHE-PSK-CHACHA20-POLY1305 TLSv1.2 Kx=DHEPSK Au=PSK Enc=CHACHA20/POLY1305(256) Mac=AEAD ECDHE-PSK-CHACHA20-POLY1305 TLSv1.2 Kx=ECDHEPSK Au=PSK Enc=CHACHA20/POLY1305(256) Mac=AEAD AES256-GCM-SHA384 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(256) Mac=AEAD PSK-AES256-GCM-SHA384 TLSv1.2 Kx=PSK Au=PSK Enc=AESGCM(256) Mac=AEAD PSK-CHACHA20-POLY1305 TLSv1.2 Kx=PSK Au=PSK Enc=CHACHA20/POLY1305(256) Mac=AEAD RSA-PSK-AES128-GCM-SHA256 TLSv1.2 Kx=RSAPSK Au=RSA Enc=AESGCM(128) Mac=AEAD DHE-PSK-AES128-GCM-SHA256 TLSv1.2 Kx=DHEPSK Au=PSK Enc=AESGCM(128) Mac=AEAD AES128-GCM-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AESGCM(128) Mac=AEAD PSK-AES128-GCM-SHA256 TLSv1.2 Kx=PSK Au=PSK Enc=AESGCM(128) Mac=AEAD AES256-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA256 AES128-SHA256 TLSv1.2 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA256 ECDHE-PSK-AES256-CBC-SHA384 TLSv1 Kx=ECDHEPSK Au=PSK Enc=AES(256) Mac=SHA384 ECDHE-PSK-AES256-CBC-SHA TLSv1 Kx=ECDHEPSK Au=PSK Enc=AES(256) Mac=SHA1 SRP-RSA-AES-256-CBC-SHA SSLv3 Kx=SRP Au=RSA Enc=AES(256) Mac=SHA1 SRP-AES-256-CBC-SHA SSLv3 Kx=SRP Au=SRP Enc=AES(256) Mac=SHA1 RSA-PSK-AES256-CBC-SHA384 TLSv1 Kx=RSAPSK Au=RSA Enc=AES(256) Mac=SHA384 DHE-PSK-AES256-CBC-SHA384 TLSv1 Kx=DHEPSK Au=PSK Enc=AES(256) Mac=SHA384 RSA-PSK-AES256-CBC-SHA SSLv3 Kx=RSAPSK Au=RSA Enc=AES(256) Mac=SHA1 DHE-PSK-AES256-CBC-SHA SSLv3 Kx=DHEPSK Au=PSK Enc=AES(256) Mac=SHA1 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 PSK-AES256-CBC-SHA384 TLSv1 Kx=PSK Au=PSK Enc=AES(256) Mac=SHA384 PSK-AES256-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=AES(256) Mac=SHA1 ECDHE-PSK-AES128-CBC-SHA256 TLSv1 Kx=ECDHEPSK Au=PSK Enc=AES(128) Mac=SHA256 ECDHE-PSK-AES128-CBC-SHA TLSv1 Kx=ECDHEPSK Au=PSK Enc=AES(128) Mac=SHA1 SRP-RSA-AES-128-CBC-SHA SSLv3 Kx=SRP Au=RSA Enc=AES(128) Mac=SHA1 SRP-AES-128-CBC-SHA SSLv3 Kx=SRP Au=SRP Enc=AES(128) Mac=SHA1 RSA-PSK-AES128-CBC-SHA256 TLSv1 Kx=RSAPSK Au=RSA Enc=AES(128) Mac=SHA256 DHE-PSK-AES128-CBC-SHA256 TLSv1 Kx=DHEPSK Au=PSK Enc=AES(128) Mac=SHA256 RSA-PSK-AES128-CBC-SHA SSLv3 Kx=RSAPSK Au=RSA Enc=AES(128) Mac=SHA1 DHE-PSK-AES128-CBC-SHA SSLv3 Kx=DHEPSK Au=PSK Enc=AES(128) Mac=SHA1 AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1 PSK-AES128-CBC-SHA256 TLSv1 Kx=PSK Au=PSK Enc=AES(128) Mac=SHA256 PSK-AES128-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=AES(128) Mac=SHA1
SRP-RSA-3DES-EDE-CBC-SHA と EDH-RSA-DES-CBC3-SHA について調べてみた
共通鍵暗号方式の一種であるDESと、その後継の3DES (Triple DES)にはSweet32という脆弱性があるらしい。
TLS、SSH、IPSec およびその他プロトコルの製品で使用される DES および Triple DES 暗号は、約 40 億ブロックの birthday bound を持っているため、平文のデータを取得される脆弱性が存在します。
本脆弱性は、"Sweet32" 攻撃と呼ばれています。
JVNDB-2016-004511 - JVN iPedia - 脆弱性対策情報データベース より引用
そして、3DES (DES-CBC3) はOpenSSL 1.1.0からデフォルトでコンパイルされなくなったようだ。
For the 1.1.0 release, which we expect to release tomorrow, we will treat triple-DES just like we are treating RC4. It is not compiled by default; you have to use “enable-weak-ssl-ciphers” as a config option. Even when those ciphers are compiled, triple-DES is only in the “MEDIUM” keyword. In addition, because this is a new release, we also removed it from the “DEFAULT” keyword.
The SWEET32 Issue, CVE-2016-2183 - OpenSSL Blog より引用
だからOpenSSL 1.1.0を使っているUbuntu 18.04ではこれらのCipher Suiteが利用できない。