Nginx 配置 HTTPS 服務器

Nginx 配置 HTTPS 服務器

by Mihan on 2016-08-16html

 

Chrome 瀏覽器地址欄標誌着 HTTPS 的綠色小鎖頭從心理層面上能夠給用戶專業安全的心理暗示,本文簡單總結一下如何在 Nginx 配置 HTTPS 服務器,讓本身站點上『綠鎖』。node

Nginx 配置 HTTPS

Nginx 配置 HTTPS 並不複雜,主要有兩個步驟:簽署第三方可信任的 SSL 證書 和 配置 HTTPSnginx

簽署第三方可信任的 SSL 證書

關於 SSL 證書

有關 SSL 的介紹能夠參閱維基百科的傳輸層安全協議和阮一峯先生的 《SSL/TLS協議運行機制的概述》git

SSL 證書主要有兩個功能:加密身份證實,一般須要購買,也有免費的,經過第三方 SSL 證書機構頒發,常見可靠的第三方 SSL 證書頒發機構有下面幾個:github

頒發 SSL 證書可靠的第三方機構

StartCom 機構上的 SSL 證書有如下幾種:golang

  • 企業級別:EV(Extended Validation)、OV(Organization Validation)
  • 我的級別:IV(Identity Validation)、DV(Domain Validation)

其中 EV、OV、IV 須要付費web

免費的證書安全認證級別通常比較低,不顯示單位名稱,不能證實網站的真實身份,僅起到加密傳輸信息的做用,適合我的網站或非電商網站。因爲此類只驗證域名全部權的低端 SSL 證書已經被國外各類欺詐網站濫用,所以強烈推薦部署驗證單位信息並顯示單位名稱的 OV SSL 證書或申請最高信任級別的、顯示綠色地址欄、直接在地址欄顯示單位名稱的 EV SSL 證書,就好像 StarCom 的地址欄同樣:算法

StarCom

更多關於購買 SSL 證書的介紹:SSL 證書服務,你們用哪家的?DV免費SSL證書瀏覽器

使用 OpenSSL 生成 SSL Key 和 CSR 文件

配置 HTTPS 要用到私鑰 example.key 文件和 example.crt 證書文件,申請證書文件的時候要用到 example.csr 文件,OpenSSL 命令能夠生成 example.key 文件和 example.csr 證書文件。緩存

  • CSR:Cerificate Signing Request,證書籤署請求文件,裏面包含申請者的 DN(Distinguished Name,標識名)和公鑰信息,在第三方證書頒發機構簽署證書的時候須要提供。證書頒發機構拿到 CSR 後使用其根證書私鑰對證書進行加密並生成 CRT 證書文件,裏面包含證書加密信息以及申請者的 DN 及公鑰信息
  • Key:證書申請者私鑰文件,和證書裏面的公鑰配對使用,在 HTTPS 『握手』通信過程須要使用私鑰去解密客戶端發來的通過證書公鑰加密的隨機數信息,是 HTTPS 加密通信過程很是重要的文件,在配置 HTTPS 的時候要用到

使用 OpenSSl命令能夠在系統當前目錄生成 example.key 和 example.csr 文

 
 

openssl req -new -newkey rsa:2048 -sha256 -nodes -out example_com.csr -keyout example_com.key -subj "/C=CN/ST=ShenZhen/L=ShenZhen/O=Example Inc./OU=Web Security/CN=example.com"

下面是上述命令相關字段含義:

  • C:Country ,單位所在國家,爲兩位數的國家縮寫,如: CN 就是中國
  • ST 字段: State/Province ,單位所在州或省
  • L 字段: Locality ,單位所在城市 / 或縣區
  • O 字段: Organization ,此網站的單位名稱;
  • OU 字段: Organization Unit,下屬部門名稱;也經常用於顯示其餘證書相關信息,如證書類型,證書產品名稱或身份驗證類型或驗證內容等;
  • CN 字段: Common Name ,網站的域名;

生成 csr 文件後,提供給 CA 機構,簽署成功後,就會獲得一個 example.crt 證書文件,SSL 證書文件得到後,就能夠在 Nginx 配置文件裏配置 HTTPS 了。

配置 HTTPS

基礎配置

要開啓 HTTPS 服務,在配置文件信息塊(server block),必須使用監聽命令 listen 的 ssl 參數和定義服務器證書文件和私鑰文件,以下所示:

 
 

server {

#ssl參數

listen 443 ssl;

server_name example.com;

#證書文件

ssl_certificate example.com.crt;

#私鑰文件

ssl_certificate_key example.com.key;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers HIGH:!aNULL:!MD5;

#...

}

證書文件會做爲公用實體發送到每臺鏈接到服務器的客戶端,私鑰文件做爲安全實體,應該被存放在具備必定權限限制的目錄文件,並保證 Nginx 主進程有存取權限

私鑰文件也有可能會和證書文件同放在一個文件中,以下面情況:

 
 

ssl_certificate www.example.com.cert;

ssl_certificate_key www.example.com.cert;

這種情況下,證書文件的的讀取權限也應該加以限制,僅管證書和私鑰存放在同一個文件裏,可是隻有證書會被髮送到客戶端

命令 ssl_protocols 和 ssl_ciphers 能夠用來限制鏈接只包含 SSL/TLS 的加強版本和算法,默認值以下:

 
 

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers HIGH:!aNULL:!MD5;

因爲這兩個命令的默認值已經好幾回發生了改變,所以不建議顯性定義,除非有須要額外定義的值,如定義 D-H 算法:

 
 

#使用DH文件

ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

#定義算法

ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

#...

HTTPS服務器優化

減小 CPU 運算量

SSL 的運行計算須要消耗額外的 CPU 資源,通常多核處理器系統會運行多個工做進程(worker processes ),進程的數量不會少於可用的 CPU 核數。SSL 通信過程當中『握手』階段的運算最佔用 CPU 資源,有兩個方法能夠減小每臺客戶端的運算量:

  • 激活 keepalive 長鏈接,一個鏈接發送更多個請求
  • 複用 SSL 會話參數,在並行併發的鏈接數中避免進行屢次 SSL『握手』

這些會話會存儲在一個 SSL 會話緩存裏面,經過命令 ssl_session_cache 配置,可使緩存在機器間共享,而後利用客戶端在『握手』階段使用的 seesion id 去查詢服務端的 session cathe(若是服務端設置有的話),簡化『握手』階段。

1M 的會話緩存大概包含 4000 個會話,默認的緩存超時時間爲 5 分鐘,能夠經過使用 ssl_session_timeout 命令設置緩存超時時間。下面是一個擁有 10M 共享會話緩存的多核系統優化配置例子:

 
 

worker_processes auto;

http {

#配置共享會話緩存大小

ssl_session_cache shared:SSL:10m;

#配置會話超時時間

ssl_session_timeout 10m;

server {

listen 443 ssl;

server_name www.example.com;

#設置長鏈接

keepalive_timeout 70;

ssl_certificate www.example.com.crt;

ssl_certificate_key www.example.com.key;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers HIGH:!aNULL:!MD5;

#...

使用 HSTS 策略強制瀏覽器使用 HTTPS 鏈接

HSTS – HTTP Strict Transport Security,HTTP嚴格傳輸安全。它容許一個 HTTPS 網站要求瀏覽器老是經過 HTTPS 來訪問,這使得攻擊者在用戶與服務器通信過程當中攔截、篡改信息以及冒充身份變得更爲困難。

只要在 Nginx 配置文件加上如下頭信息就能夠了:

 
 

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;preload" always;

  • max-age:設置單位時間內強制使用 HTTPS 鏈接
  • includeSubDomains:可選,全部子域同時生效
  • preload:可選,非規範值,用於定義使用『HSTS 預加載列表』
  • always:可選,保證全部響應都發送此響應頭,包括各類內置錯誤響應

當用戶進行 HTTPS 鏈接的時候,服務器會發送一個 Strict-Transport-Security 響應頭:

STS Header

瀏覽器在獲取該響應頭後,在 max-age 的時間內,若是遇到 HTTP 鏈接,就會經過 307 跳轉強制使用 HTTPS 進行鏈接,並忽略其它的跳轉設置(如 301 重定向跳轉):

HSTS

307 跳轉 Non-Authoritative-Reason 響應頭

HSTS

Google HSTS 預加載列表(HSTS Preload List)

因爲 HSTS 須要用戶通過一次安全的 HTTPS 鏈接後纔會在 max-age 的時間內生效,所以HSTS 策略並不能完美防止 HTTP 會話劫持(HTTP session hijacking),在下面這些情況下仍是存在被劫持的可能:

  • 從未訪問過的網站
  • 近期重裝過操做系統
  • 近期重裝過瀏覽器
  • 使用新的瀏覽器
  • 使用了新的設備(如手機)
  • 刪除了瀏覽器緩存
  • 近期沒有打開過網站且 max-age 過時

針對這種情況,Google 維護了一份『HSTS 預加載列表』,列表裏包含了使用了 HSTS 的站點主域名和子域名,能夠經過如下頁面申請加入:

https://hstspreload.appspot.com/.

申請的時候會先驗證站點是否符合資格,通常會檢驗待驗證的站點主域和子域是否能經過 HTTPS 鏈接、HTTPS 和 HTTP 配置是否有 STS Header 等信息,經過驗證後,會讓你確認一些限制信息,以下圖:

HSTS

當確認提交後,就會顯示處理狀態:

HSTS

申請經過後,列表內的站點名會被寫進主流的瀏覽器,當瀏覽器更新版本後,只要打開列表內的站點,瀏覽器會拒絕全部 HTTP 鏈接而自動使用 HTTPS,即便關閉了 HSTS 設置。

能夠在下面兩個鏈接分別查找 Chrome 和 Firfox 的『HSTS 預加載列表』內容:

The Chromium Projects - HTTP Strict Transport Security

Firefox HSTS preload list - nsSTSPreloadList.inc

須要注意的是:

  • 一旦把本身的站點名加入『HSTS 預加載列表』,將很難完全從列表中移除,由於不能保證其它瀏覽器能夠及時移除,即便 Chrome 提供有便捷的移除方法,也是要經過出郵件聯繫,註明移除緣由,並等到最新的瀏覽器版本更新發布纔有機會(用戶不必定會及時更新)
  • 全部不具有有效證書的子域或內嵌子域的訪問將會被阻止

所以,若是本身站點子域名變化比較多,又沒有泛域證書,又沒法肯定全站是否能應用 HTTPS 的朋友,就要謹慎申請了。

更多關於 HSTS 配置可參考:

《HTTP Strict Transport Security (HSTS) and NGINX》

MDN的《HTTP Strict Transport Security》

瀏覽器兼容性

Compatibility

增強 HTTPS 安全性

HTTPS 基礎配置採起的默認加密算法是 SHA-1,這個算法很是脆弱,安全性在逐年下降,在 2014 年的時候, Google 官方博客就宣佈在 Chrome 瀏覽器中逐漸下降 SHA-1 證書的安全指示,會從 2015 年起使用 SHA-2 簽名的證書,可參閱 Rabbit_Run 在 2014 年發表的文章:《爲何Google急着殺死加密算法SHA-1》

爲此,主流的 HTTPS 配置方案應該避免 SHA-1,可使用 迪菲-赫爾曼密鑰交換(D-H,Diffie–Hellman key exchange)方案。

首先在目錄 /etc/ssl/certs 運行如下代碼生成 dhparam.pem 文件:

 
 

openssl dhparam -out dhparam.pem 2048

而後加入 Nginx 配置:

 
 

#優先採起服務器算法

ssl_prefer_server_ciphers on;

#使用DH文件

ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

#定義算法

ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

若是服務器夠強大,可使用更爲複雜的 4096 位進行加密。

通常情況下還應該加上如下幾個加強安全性的命令:

 
 

#減小點擊劫持

add_header X-Frame-Options DENY;

#禁止服務器自動解析資源類型

add_header X-Content-Type-Options nosniff;

#防XSS攻擊

add_header X-Xss-Protection 1;

這幾個安全命令在 Jerry Qu 大神的文章《一些安全相關的HTTP響應頭》有詳細的介紹。

優化後的綜合配置

 

worker_processes auto;

http {

#配置共享會話緩存大小,視站點訪問狀況設定

ssl_session_cache shared:SSL:10m;

#配置會話超時時間

ssl_session_timeout 10m;

server {

listen 443 ssl;

server_name www.example.com;

#設置長鏈接

keepalive_timeout 70;

#HSTS策略

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

#證書文件

ssl_certificate www.example.com.crt;

#私鑰文件

ssl_certificate_key www.example.com.key;

#優先採起服務器算法

ssl_prefer_server_ciphers on;

#使用DH文件

ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

#定義算法

ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

#減小點擊劫持

add_header X-Frame-Options DENY;

#禁止服務器自動解析資源類型

add_header X-Content-Type-Options nosniff;

#防XSS攻擊

add_header X-Xss-Protection 1;

#...

HTTP/HTTPS混合服務器配置

能夠同時配置 HTTP 和 HTTPS 服務器:

 
 

server {

listen 80;

listen 443 ssl;

server_name www.example.com;

ssl_certificate www.example.com.crt;

ssl_certificate_key www.example.com.key;

#...

}

在 0.7.14 版本以前,在獨立的 server 端口中是不能選擇性開啓 SSL 的。如上面的例子,SSL 只能經過使用 ssl 命令爲單個 server 端口開啓

 
 

server {

listen 443;

server_name www.example.com;

ssl_certificate www.example.com.crt;

ssl_certificate_key www.example.com.key;

#ssl命令開啓 https

ssl on;

#...

}

所以沒有辦法設置 HTTP/HTTPS 混合服務器。因而 Nginx 新增了監聽命令 listen參數 ssl 來解決這個問題,Nginx 現代版本的ssl命令並不推薦使用

基於服務器名稱(name-based)的 HTTPS 服務器

一個常見的問題就是當使用同一個 IP 地址去配置兩個或更多的 HTTPS 服務器的時候,出現證書不匹配的情況:

 
 

server {

listen 443 ssl;

server_name www.example.com;

ssl_certificate www.example.com.crt;

#...

}

server {

listen 443 ssl;

server_name www.example.org;

ssl_certificate www.example.org.crt;

#...

}

這種狀況下瀏覽器會獲取默認的服務器證書(如上面例子的 www.example.com.crt)而忽視請求的服務器名,如輸入網址:www.example.org,服務器會發送 www.example.com.crt 的證書到客戶端,而不是 www.exaple.org.crt

這是由於 SSL 協議行爲所致,SSL 鏈接在瀏覽器發送 HTTP 請求以前就被創建,Nginx 並不知道被請求的服務器名字,所以 Nginx 只會提供默認的服務器證書。

證書獲取異常

解決這個問題最原始最有效的方法就是爲每一個 HTTPS 服務器分配獨立的 IP 地址:

 
 

server {

listen 192.168.1.1:443 ssl;

server_name www.example.com;

ssl_certificate www.example.com.crt;

#...

}

server {

listen 192.168.1.2:443 ssl;

server_name www.example.org;

ssl_certificate www.example.org.crt;

#...

}

更多解決方案

除此以外,官方還介紹了兩個方法:泛域證書域名指示(SNI)

其實 OpenSSL 在 0.9.8f版本就支持 SNI 了,只要在安裝的時候加上 --enable-tlsext 選項就能夠。到了 0.9.8j版本,這個選項在安裝的時候會默認啓用。若是建立 Nginx 的時候支持 SNI,能夠在 Nginx 版本信息查到如下的字段:

 
 

TLS SNI support enabled

所以,若是較新版本的 Nginx 使用默認的 OpenSSL 庫,是不存在使用 HTTPS 同時支持基於名字的虛擬主機的時候同 IP 不一樣域名證書不匹配的問題

注意:即便新版本的 Nginx 在建立時支持了 SNI,若是 Nginx 動態加載不支持 SNI 的 OpenSSL 庫的話,SNI 擴展將不可用

有興趣的朋友能夠看下:

An SSL certificate with several names && Server Name Indication

總結

OK,咱們簡單總結一下在 Nginx 下配置 HTTPS 的關鍵要點:

  • 得到 SSL 證書

    • 經過 OpenSSL 命令得到 example.key 和 example.csr 文件
    • 提供 example.csr 文件給第三方可靠證書頒發機構,選擇適合的安全級別證書並簽署,得到 example.crt 文件
  • 經過 listen 命令 SSL 參數以及引用 example.key 和 example.crt 文件完成 HTTPS 基礎配置

  • HTTPS優化

    • 減小 CPU 運算量
      • 使用 keepalive 長鏈接
      • 複用 SSL 會話參數
    • 使用 HSTS 策略強制瀏覽器使用 HTTPS 鏈接
      • 添加 Strict-Transport-Security 頭部信息
      • 使用 HSTS 預加載列表(HSTS Preload List)
    • 增強 HTTPS 安全性
      • 使用迪菲-赫爾曼密鑰交換(D-H,Diffie–Hellman key exchange)方案
      • 添加 X-Frame-Options 頭部信息,減小點擊劫持
      • 添加 X-Content-Type-Options 頭部信息,禁止服務器自動解析資源類型
      • 添加 X-Xss-Protection 頭部信息,防XSS攻擊
  • HTTP/HTTPS混合服務器配置

  • 基於服務器名稱(name-based)的 HTTPS 服務器

    • 爲每一個 HTTPS 服務器分配獨立的 IP 地址
    • 泛域證書
    • 域名標識(SNI)

其實簡單的我的博客,若是沒有敏感數據交互的話,使用 http 協議通信,通常都夠用了,頁面速度還會更快,但正如文章開頭所說,戴上『綠鎖』,更專業更安全~~有興趣的同窗能夠去深刻了解折騰下:)

嘻~~

感謝您的閱讀,本文由 凹凸實驗室 版權全部。如若轉載,請註明出處:凹凸實驗室(https://aotu.io/notes/2016/08/16/nginx-https/

相關文章
相關標籤/搜索