介紹如何設定 Nginx 網頁伺服器,停用不安全的 TLS 1.1 與 TLS 1.0 協定,只使用 TLS 1.2 與 TLS 1.3 協定。
TLS 傳輸層安全性協定(Transport Layer Security)是一種加密的傳輸協定,廣泛用於網頁、電子郵件、即時通訊等各類應用的資料傳輸,而 TLS 有許多版本,在美國國家安全局(National Security Agency,NSA)發布的安全指引中,建議企業停用 TLS 1.0、1.1 與更早期的 SSL 2.0、3.0 協定。
以下是在 Ubuntu Linux 中設定 Nginx 伺服器停用 TLS 1.1 與 1.0,只使用 TLS 1.2 與 1.3 協定的方法。
檢查 Nginx 與 OpenSSL 版本
若要讓 Nginx 支援 TLS 1.3,Nginx 與 OpenSSL 的版本必須滿足以下條件:
- Nginx 1.13 或更新版本。
- Nginx 必須搭配 OpenSSL 1.1.1 或更新版本編譯。
若在 Ubuntu Linux 18.10 或 20.04 以後的版本中,官方套件庫的 Nginx 都已經符合這些條件,所以直接使用 apt
安裝的 Nginx 伺服器就可以直接使用 TLS 1.2 與 1.3。
若要查詢 Nginx 與 OpenSSL 的版本,可以使用以下指令:
# 查詢 Nginx 版本與編譯資訊 nginx -V
nginx version: nginx/1.14.0 (Ubuntu) built with OpenSSL 1.1.1 11 Sep 2018 TLS SNI support enabled configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-H4cN7P/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-H4cN7P/nginx-1.14.0/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-H4cN7P/nginx-1.14.0/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-H4cN7P/nginx-1.14.0/debian/modules/http-echo --add-dynamic-module=/build/nginx-H4cN7P/nginx-1.14.0/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-H4cN7P/nginx-1.14.0/debian/modules/http-subs-filter
若要檢查系統上 OpenSSL 的版本,可以執行:
# 查詢系統上的 OpenSSL 版本
openssl version
OpenSSL 1.1.1 11 Sep 2018
設定 Nginx 只使用 TLS 1.2 與 TLS 1.3
若要讓 Nginx 僅採用 TLS 1.2 協定,可在 Nginx 設定檔的 http
或 server
區塊中,加入 ssl_protocols
設定,指定採用 TLS 1.2 協定:
server { # ... ssl_protocols TLSv1.2; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # ... }
若要讓 Nginx 可以同時支援 TLS 1.2 與 1.3,可以在 ssl_protocols
的設定上同時指定這兩種協定:
server { # ... ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # ... }
若想要強制 Nginx 只使用 TLS 1.3,則可以這樣設定:
server { # ... ssl_protocols TLSv1.3; ssl_prefer_server_ciphers off; # ... }
修改好 Nginx 設定檔之後,記得要先測試一下設定檔是否有問題:
# 測試 Nginx 設定 sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
確認設定檔沒問題之後,就可以重新啟動 Nginx 伺服器:
# 重新啟動 Nginx 伺服器
sudo systemctl restart nginx
測試 Nginx 伺服器 TLS 版本
若要測試 Nginx 伺服器的 TLS 版本,可以使用 curl
指令:
# 使用 TLS 1.2 進行連線 curl -I -v --tlsv1.2 --tls-max 1.2 https://officeguide.cc/ # 使用 TLS 1.3 進行連線 curl -I -v --tlsv1.3 --tls-max 1.3 https://officeguide.cc/
若可以使用 TLS 1.3 正常建立連線的話,輸出會類似這樣:
* Trying 139.162.102.70... * TCP_NODELAY set * Connected to officeguide.cc (139.162.102.70) 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 Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Unknown (8): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Client hello (1): * TLSv1.3 (OUT), TLS Unknown, Certificate Status (22): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=officeguide.cc * start date: Jul 20 17:56:12 2021 GMT * expire date: Oct 18 17:56:10 2021 GMT * subjectAltName: host "officeguide.cc" matched cert's "officeguide.cc" * issuer: C=US; O=Let's Encrypt; CN=R3 * 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 * TLSv1.3 (OUT), TLS Unknown, Unknown (23): * TLSv1.3 (OUT), TLS Unknown, Unknown (23): * TLSv1.3 (OUT), TLS Unknown, Unknown (23): * Using Stream ID: 1 (easy handle 0x55b02a88a600) * TLSv1.3 (OUT), TLS Unknown, Unknown (23): > HEAD / HTTP/2 > Host: officeguide.cc > User-Agent: curl/7.58.0 > Accept: */* > * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS Unknown, Certificate Status (22): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS Unknown, Unknown (23): * Connection state changed (MAX_CONCURRENT_STREAMS updated)! * TLSv1.3 (OUT), TLS Unknown, Unknown (23): * TLSv1.3 (IN), TLS Unknown, Unknown (23): * TLSv1.3 (IN), TLS Unknown, Unknown (23): < HTTP/2 200 HTTP/2 200 < server: nginx server: nginx < date: Mon, 13 Sep 2021 10:08:30 GMT date: Mon, 13 Sep 2021 10:08:30 GMT < content-type: text/html; charset=UTF-8 content-type: text/html; charset=UTF-8 < vary: Accept-Encoding vary: Accept-Encoding < x-powered-by: PHP/7.3.29 x-powered-by: PHP/7.3.29 < wpo-cache-status: not cached wpo-cache-status: not cached < wpo-cache-message: The request method was not GET (HEAD) wpo-cache-message: The request method was not GET (HEAD) < link: <https://officeguide.cc/wp-json/>; rel="https://api.w.org/" link: <https://officeguide.cc/wp-json/>; rel="https://api.w.org/" < expires: Mon, 20 Sep 2021 10:08:30 GMT expires: Mon, 20 Sep 2021 10:08:30 GMT < cache-control: max-age=604800 cache-control: max-age=604800 < x-cache: HIT x-cache: HIT < * Connection #0 to host officeguide.cc left intact
若採用不安全的 TLS 1.0 與 1.1,正常應該要無法建立連線:
# 使用 TLS 1.1 進行連線 curl -I -v --tlsv1.1 --tls-max 1.1 https://officeguide.cc/ # 使用 TLS 1.0 進行連線 curl -I -v --tlsv1.0 --tls-max 1.0 https://officeguide.cc/
無法建立 TLS 1.0 的連線時,輸出會類似這樣:
* Trying 139.162.102.70... * TCP_NODELAY set * Connected to officeguide.cc (139.162.102.70) 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.0 (OUT), TLS handshake, Client hello (1): * TLSv1.0 (IN), TLS alert, Server hello (2): * error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version * stopped the pause stream! * Closing connection 0 curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version