🔒

TLSのversionを確認して、特定のバージョンでの動作確認を行う

2022/12/04に公開

備忘録としてまとめておきます。
特定の SSL/TLS のバージョンがサポートされているかを確認しつつ、動作確認をする (e.g. HTTPS Request が 200 OK を返す) ようなケースを想定しています。

TLSのversionを確認する

nmap の ssl-enum-ciphers のスクリプトを利用します。
https://nmap.org/nsedoc/scripts/ssl-enum-ciphers.html

❯ nmap -V              
Nmap version 7.93 ( https://nmap.org )
Platform: arm-apple-darwin21.6.0
Compiled with: liblua-5.3.6 openssl-1.1.1s libssh2-1.10.0 libz-1.2.11 libpcre-8.45 libpcap-1.9.1 nmap-libdnet-1.12 ipv6
Compiled without:
Available nsock engines: kqueue poll select

❯ nmap -h | grep script
  -sC: equivalent to --script=default
  --script=<Lua scripts>: <Lua scripts> is a comma separated list of
           directories, script-files or script-categories
  --script-args=<n1=v1,[n2=v2,...]>: provide arguments to scripts
  --script-args-file=filename: provide NSE script args in a file
  --script-trace: Show all data sent and received
  --script-updatedb: Update the script database.
  --script-help=<Lua scripts>: Show help about scripts.
           <Lua scripts> is a comma-separated list of script-files or
           script-categories.
  -A: Enable OS detection, version detection, script scanning, and traceroute

オプションにも記載のある通り、script は Lua で実装されています。
https://github.com/nmap/nmap/blob/master/nse_main.lua

出力結果は下記のような感じになります。

❯ nmap -sV --script ssl-enum-ciphers -p 443 google.com       
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-04 20:28 JST
Nmap scan report for google.com (142.250.196.110)
Host is up (0.0049s latency).
rDNS record for 142.250.196.110: nrt12s35-in-f14.1e100.net

PORT    STATE SERVICE   VERSION
443/tcp open  ssl/https gws
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Date: Sun, 04 Dec 2022 11:28:15 GMT
|     Expires: -1
|     Cache-Control: private, max-age=0
|     Content-Type: text/html; charset=ISO-8859-1
|     Cross-Origin-Opener-Policy-Report-Only: same-origin-allow-popups; report-to="gws"
|     Report-To: {"group":"gws","max_age":2592000,"endpoints":[{"url":"https://csp.withgoogle.com/csp/report-to/gws/other"}]}
|     P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
|     Server: gws
|     X-XSS-Protection: 0
|     X-Frame-Options: SAMEORIGIN
|     Set-Cookie: 1P_JAR=2022-12-04-11; expires=Tue, 03-Jan-2023 11:28:15 GMT; path=/; domain=.google.com; Secure
|     Set-Cookie: AEC=AakniGNu2qVzUr8xeccGgO2VOlq7QNTuPkN62NtAm7yp3nnddi9bXXXSfl0; expires=Fri, 02-Jun-2023 11:28:15 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
|     Set-Cookie: NID=511=ZP4aD2RiDXlGS0q90MhKG8ZAtU6YoarsnKdhzytOoy7S2Shhgu65ErVZ9oyWpKCYrFvCDTX57whRhZI_uP--28blHmyKk6nHowwA8MaD6AU7gRF4
|   HTTPOptions: 
|     HTTP/1.0 405 Method Not Allowed
|     Allow: GET, HEAD
|     Date: Sun, 04 Dec 2022 11:28:15 GMT
|     Content-Type: text/html; charset=UTF-8
|     Server: gws
|     Content-Length: 1592
|     X-XSS-Protection: 0
|     X-Frame-Options: SAMEORIGIN
|     Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
|     <!DOCTYPE html>
|     <html lang=en>
|     <meta charset=utf-8>
|     <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
|     <title>Error 405 (Method Not Allowed)!!1</title>
|     <style>
|_    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22p
|_http-server-header: gws
| ssl-enum-ciphers: 
|   TLSv1.0: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|     compressors: 
|       NULL
|     cipher preference: server
|     warnings: 
|       64-bit block cipher 3DES vulnerable to SWEET32 attack
|   TLSv1.1: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|     compressors: 
|       NULL
|     cipher preference: server
|     warnings: 
|       64-bit block cipher 3DES vulnerable to SWEET32 attack
|   TLSv1.2: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|       TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: client
|     warnings: 
|       64-bit block cipher 3DES vulnerable to SWEET32 attack
|   TLSv1.3: 
|     ciphers: 
|       TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|       TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
|     cipher preference: client
|_  least strength: C
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port443-TCP:V=7.93%T=SSL%I=7%D=12/4%Time=638C844F%P=arm-apple-darwin21.
SF:6.0%r(GetRequest,1AEA,"HTTP/1\.0\x20200\x20OK\r\nDate:\x20Sun,\x2004\x2
SF:0Dec\x202022\x2011:28:15\x20GMT\r\nExpires:\x20-1\r\nCache-Control:\x20
SF:private,\x20max-age=0\r\nContent-Type:\x20text/html;\x20charset=ISO-885
SF:9-1\r\nCross-Origin-Opener-Policy-Report-Only:\x20same-origin-allow-pop
SF:ups;\x20report-to=\"gws\"\r\nReport-To:\x20{\"group\":\"gws\",\"max_age
SF:\":2592000,\"endpoints\":\[{\"url\":\"https://csp\.withgoogle\.com/csp/
SF:report-to/gws/other\"}\]}\r\nP3P:\x20CP=\"This\x20is\x20not\x20a\x20P3P
SF:\x20policy!\x20See\x20g\.co/p3phelp\x20for\x20more\x20info\.\"\r\nServe
SF:r:\x20gws\r\nX-XSS-Protection:\x200\r\nX-Frame-Options:\x20SAMEORIGIN\r
SF:\nSet-Cookie:\x201P_JAR=2022-12-04-11;\x20expires=Tue,\x2003-Jan-2023\x
SF:2011:28:15\x20GMT;\x20path=/;\x20domain=\.google\.com;\x20Secure\r\nSet
SF:-Cookie:\x20AEC=AakniGNu2qVzUr8xeccGgO2VOlq7QNTuPkN62NtAm7yp3nnddi9bXXX
SF:Sfl0;\x20expires=Fri,\x2002-Jun-2023\x2011:28:15\x20GMT;\x20path=/;\x20
SF:domain=\.google\.com;\x20Secure;\x20HttpOnly;\x20SameSite=lax\r\nSet-Co
SF:okie:\x20NID=511=ZP4aD2RiDXlGS0q90MhKG8ZAtU6YoarsnKdhzytOoy7S2Shhgu65Er
SF:VZ9oyWpKCYrFvCDTX57whRhZI_uP--28blHmyKk6nHowwA8MaD6AU7gRF4")%r(HTTPOpti
SF:ons,7BC,"HTTP/1\.0\x20405\x20Method\x20Not\x20Allowed\r\nAllow:\x20GET,
SF:\x20HEAD\r\nDate:\x20Sun,\x2004\x20Dec\x202022\x2011:28:15\x20GMT\r\nCo
SF:ntent-Type:\x20text/html;\x20charset=UTF-8\r\nServer:\x20gws\r\nContent
SF:-Length:\x201592\r\nX-XSS-Protection:\x200\r\nX-Frame-Options:\x20SAMEO
SF:RIGIN\r\nAlt-Svc:\x20h3=\":443\";\x20ma=2592000,h3-29=\":443\";\x20ma=2
SF:592000,h3-Q050=\":443\";\x20ma=2592000,h3-Q046=\":443\";\x20ma=2592000,
SF:h3-Q043=\":443\";\x20ma=2592000,quic=\":443\";\x20ma=2592000;\x20v=\"46
SF:,43\"\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=en>\n\x20\x20<meta\x20ch
SF:arset=utf-8>\n\x20\x20<meta\x20name=viewport\x20content=\"initial-scale
SF:=1,\x20minimum-scale=1,\x20width=device-width\">\n\x20\x20<title>Error\
SF:x20405\x20\(Method\x20Not\x20Allowed\)!!1</title>\n\x20\x20<style>\n\x2
SF:0\x20\x20\x20\*{margin:0;padding:0}html,code{font:15px/22px\x20arial,sa
SF:ns-serif}html{background:#fff;color:#222;padding:15px}body{margin:7%\x2
SF:0auto\x200;max-width:390px;min-height:180px;padding:30px\x200\x2015px}\
SF:*\x20>\x20body{background:url\(//www\.google\.com/images/errors/robot\.
SF:png\)\x20100%\x205px\x20no-repeat;padding-right:205px}p{margin:11px\x20
SF:0\x2022p");

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 75.19 seconds

TLSの特定のバージョンでHTTPS requestを送信する

curl で --tls-max--tlsのオプションを利用して、TLS バージョンの上限・下限を設定して、HTTP Request を送信します。
--tlsv1.xのオプションだけでは下限のバージョンのみ設定するので、SSL/TLS handshake の中で client, server の両方が対応している version の中で最も高い TLS バージョンが選択されてしまうことがあります。

curl --help all | grep tls
     --proxy-tls13-ciphers <ciphersuite list>  TLS 1.3 proxy cipher suites
     --proxy-tlsauthtype <type>  TLS authentication type for HTTPS proxy
     --proxy-tlspassword <string>  TLS password for HTTPS proxy
     --proxy-tlsuser <name>  TLS username for HTTPS proxy
     --proxy-tlsv1   Use TLSv1 for HTTPS proxy
     --tls-max <VERSION>  Set maximum allowed TLS version
     --tls13-ciphers <ciphersuite list>  TLS 1.3 cipher suites to use
     --tlsauthtype <type>  TLS authentication type
     --tlspassword   TLS password
     --tlsuser <name>  TLS user name
 -1, --tlsv1         Use TLSv1.0 or greater
     --tlsv1.0       Use TLSv1.0 or greater
     --tlsv1.1       Use TLSv1.1 or greater
     --tlsv1.2       Use TLSv1.2 or greater
     --tlsv1.3       Use TLSv1.3 or greater

出力結果は下記のような感じになります。

curl -s -v --tlsv1.0 --tls-max 1.0 -o /dev/null https://cloud.google.com 
*   Trying 142.250.207.46:443...
* Connected to cloud.google.com (142.250.207.46) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* TLSv1.0 (OUT), TLS handshake, Client hello (1):
} [152 bytes data]
* TLSv1.0 (IN), TLS handshake, Server hello (2):
{ [96 bytes data]
* TLSv1.0 (IN), TLS handshake, Certificate (11):
{ [6448 bytes data]
* TLSv1.0 (IN), TLS handshake, Server key exchange (12):
{ [112 bytes data]
* TLSv1.0 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.0 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.0 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.0 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.0 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.0 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1 / ECDHE-ECDSA-AES128-SHA
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=*.google.com
*  start date: Nov  2 13:43:09 2022 GMT
*  expire date: Jan 25 13:43:08 2023 GMT
*  subjectAltName: host "cloud.google.com" matched cert's "*.google.com"
*  issuer: C=US; O=Google Trust Services LLC; CN=GTS CA 1C3
*  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 0x15900ca00)
> GET / HTTP/2
> Host: cloud.google.com
> user-agent: curl/7.77.0
> accept: */*
> 
< HTTP/2 200 
< last-modified: Thu, 01 Dec 2022 22:23:58 GMT
< content-type: text/html; charset=utf-8
< set-cookie: _ga_devsite=GA1.3.2610108273.1670154189; Expires=Tue, 03 Dec 2024 11:43:09 GMT; Max-Age=63072000; Path=/
< content-security-policy: base-uri 'self'; object-src 'none'; script-src 'strict-dynamic' 'unsafe-inline' https: http: 'nonce-3RcjSdfu/X9S9kfGL98ZUwnoovwgpD' 'unsafe-eval'; report-uri https://csp.withgoogle.com/csp/devsite/v2
< strict-transport-security: max-age=63072000; includeSubdomains; preload
< x-frame-options: SAMEORIGIN
< x-xss-protection: 0
< x-content-type-options: nosniff
< cache-control: no-cache, must-revalidate
< expires: 0
< pragma: no-cache
< x-cloud-trace-context: a33112136a275d094a5b5c0f1572055e
< date: Sun, 04 Dec 2022 11:43:09 GMT
< server: Google Frontend
< content-length: 914180
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
< 
{ [1350 bytes data]
* Connection #0 to host cloud.google.com left intact

Discussion