閱讀310 返回首頁    go 阿裏雲 go 技術社區[雲棲]


阿裏雲 CDN HTTPS 最佳實踐——動態密鑰套件(三)

背景

在 ssllabs 中可以測試域名的 SSL 安全等級:

https3_1

影響這個測試等級的最主要因素就是密鑰套件,在接入阿裏雲 CDN 的所有域名中,絕大多數域名評級都是 A,但是有少數域名為了兼容一些老瀏覽器或者客戶端,需要支持比如 RC4 這樣的加密算法,這樣就導致評級為 B,但用戶體驗更重要,這就需要為這些對密鑰套件有特殊需求的域名特殊配置密鑰套件。

另外,當我們調試 https 時,比如抓包分析數據包時,發現應用數據都是加密的,無法分析 HTTP 協議的問題,但是如果我們有私鑰,那就有辦法可以通過 wireshark 來配置解密抓包信息。但是這個有個前提條件是密鑰交換算法是 RSA,如果使用 ECDHE 即使有私鑰也是無法解密抓包文件的,這就是所謂的 PFS(Perfect Forward Secrecy)。所以當我們想調試 HTTPS 裏麵的明文信息時,臨時調整一下服務器的密鑰套件列表中密鑰交換算法僅包含 RSA 即可,調試完成之後再恢複成 PFS 密鑰套件,這樣就很方便的調試 HTTPS 了。

原理

密鑰套件也稱加密套件、密碼套件,是 SSL 中最核心的算法套件,稱之為套件是因為其中包含了密鑰交換算法、簽名算法、加密算法和摘要算法,由兩個字節表示。以下是 TLS 密鑰套件的語法:

https3_2

在 Client Hello 中有密鑰套件列表,表示客戶端所支持的密鑰套件:

http3_3

服務端隻能從 Client Hello 的密鑰套件列表中選擇一個做為本次會話的密鑰套件:

https3_4

那服務端又是怎麼選擇這個密鑰套件呢?這就涉及到密鑰套件選擇算法了,在 openssl 裏麵密鑰套件的選擇與幾個因素有關:

  1. 服務端配置的密鑰套件列表。
  2. 客戶端支持的密鑰套件列表。
  3. 服務端密鑰套件列表優先還是客戶端密鑰套件列表優先。
  4. 已選擇的 SSL 協議版本。
  5. 證書所支持的算法。

如果服務端密鑰套件列表優先,則挨個從服務端配置的密鑰套件列表中選擇一個密鑰套件是證書所支持的、已選擇的 SSL 協議版本所支持的,並且在客戶端密鑰套件列表的密鑰套件,如果是客戶端密鑰套件列表優化也是類似,一般都是配置服務端密鑰套件優先。

其中最關鍵的就是服務端配置的密鑰套件列表,下麵先來看看 openssl 中密鑰套件的表達方式:

openssl ciphers 'ALL' -V

https3_6

上麵的協議版本就是該密鑰套件支持的協議最低版本,比如 ECDHE-RSA-AES128-GCM-SHA256 的協議版本是 TLSv1.2,那如果握手時選擇了 TLSv1.1、TLSv1.0、SSLv3 這些版本則不能使用該密鑰套件。

    密鑰套件之間用4個分隔符之一分開——冒號、分號、逗號或者空格,一般使用冒號。如下密鑰套件列表中包含了所有特殊字符:

EECDH+AESGCM:+EECDH+AES256:ALL:-RSA+3DES:!DH:!PSK:!KRB5:!MD5:!RC4:!aNULL:!EXP:!LOW:!SSLV2:!NULL

其說明如下:

EECDH+AESGCM:同時包含EECDH和AESGCM算法的密鑰套件,+號相當於『與』操作。  
密鑰套件前麵+號:將該算法移到算法列表的末尾。這個選項不會添加任何新的算法,它隻是緊緊的移動匹配的已經存在的算法。  
密鑰套件前麵-號:從算法列表中刪除該算法。但是可以通過後麵的選項將一個或所有的算法可以被再次添加。  
密鑰套件前麵!號:從算法列表中刪除該算法。刪除了的算法將不會再出現。

通過 openssl ciphers 指令列出來的順序就是服務端的密鑰套件列表順序:

$openssl ciphers 'EECDH+AESGCM:+EECDH+AES256:ALL:-RSA+3DES:!DH:!PSK:!KRB5:!MD5:!RC4:!aNULL:!EXP:!LOW:!SSLV2:!NULL' -V
          0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(128) Mac=AEAD
          0xC0,0x2B - ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(128) Mac=AEAD
          0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
          0xC0,0x2C - ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
          0xC0,0x28 - ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
          0xC0,0x24 - ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
          0xC0,0x14 - ECDHE-RSA-AES256-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1
          0xC0,0x0A - ECDHE-ECDSA-AES256-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1
          0xC0,0x32 - ECDH-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AESGCM(256) Mac=AEAD
          0xC0,0x2E - ECDH-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AESGCM(256) Mac=AEAD
          0xC0,0x2A - ECDH-RSA-AES256-SHA384  TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AES(256)  Mac=SHA384
          0xC0,0x26 - ECDH-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256)  Mac=SHA384
          0xC0,0x0F - ECDH-RSA-AES256-SHA     SSLv3 Kx=ECDH/RSA Au=ECDH Enc=AES(256)  Mac=SHA1
          0xC0,0x05 - ECDH-ECDSA-AES256-SHA   SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256)  Mac=SHA1
          0x00,0x9D - AES256-GCM-SHA384       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(256) Mac=AEAD
          0x00,0x3D - AES256-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA256
          0x00,0x35 - AES256-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA1
          0x00,0x84 - CAMELLIA256-SHA         SSLv3 Kx=RSA      Au=RSA  Enc=Camellia(256) Mac=SHA1
          0xC0,0x27 - ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA256
          0xC0,0x23 - ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA256
          0xC0,0x13 - ECDHE-RSA-AES128-SHA    SSLv3 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA1
          0xC0,0x09 - ECDHE-ECDSA-AES128-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA1
          0xC0,0x12 - ECDHE-RSA-DES-CBC3-SHA  SSLv3 Kx=ECDH     Au=RSA  Enc=3DES(168) Mac=SHA1
          0xC0,0x08 - ECDHE-ECDSA-DES-CBC3-SHA SSLv3 Kx=ECDH     Au=ECDSA Enc=3DES(168) Mac=SHA1
          0xC0,0x31 - ECDH-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AESGCM(128) Mac=AEAD
          0xC0,0x2D - ECDH-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AESGCM(128) Mac=AEAD
          0xC0,0x29 - ECDH-RSA-AES128-SHA256  TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AES(128)  Mac=SHA256
          0xC0,0x25 - ECDH-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AES(128)  Mac=SHA256
          0xC0,0x0E - ECDH-RSA-AES128-SHA     SSLv3 Kx=ECDH/RSA Au=ECDH Enc=AES(128)  Mac=SHA1
          0xC0,0x04 - ECDH-ECDSA-AES128-SHA   SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=AES(128)  Mac=SHA1
          0xC0,0x0D - ECDH-RSA-DES-CBC3-SHA   SSLv3 Kx=ECDH/RSA Au=ECDH Enc=3DES(168) Mac=SHA1
          0xC0,0x03 - ECDH-ECDSA-DES-CBC3-SHA SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=3DES(168) Mac=SHA1
          0x00,0x9C - AES128-GCM-SHA256       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(128) Mac=AEAD
          0x00,0x3C - AES128-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA256
          0x00,0x2F - AES128-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA1
          0x00,0x96 - SEED-SHA                SSLv3 Kx=RSA      Au=RSA  Enc=SEED(128) Mac=SHA1
          0x00,0x41 - CAMELLIA128-SHA         SSLv3 Kx=RSA      Au=RSA  Enc=Camellia(128) Mac=SHA1
          0x00,0x07 - IDEA-CBC-SHA            SSLv3 Kx=RSA      Au=RSA  Enc=IDEA(128) Mac=SHA1

實現

在 Tengine 中有兩個指令跟密鑰套件有關:

ssl_ciphers                  EECDH+AESGCM:+EECDH+AES256:ALL:-RSA+3DES:!DH:!PSK:!KRB5:!MD5:!RC4:!aNULL:!EXP:!LOW:!SSLV2:!NULL;
ssl_prefer_server_ciphers    on;

其中,ssl_ciphers 是配置服務器支持的密鑰套件列表,ssl_prefer_server_ciphers 為 on 則指定從服務器支持的密鑰套件列表中的優先順序來選擇密鑰套件,為 off 則表示從客戶端支持的密鑰套件列表中的優先順序來選擇密鑰套件。

ssl_prefer_server_ciphers 配置為 on 是必須的,因為如果不設置為 on,那意味著優先從客戶端支持的密鑰套件列表中選擇加密套件,這樣的話可能導致 SSL 的性能比較低,而且可能導致被攻擊。

另外,我們知道 ssl_ciphers 指令是靜態配置,想要修改服務器支持的密鑰套件列表的話,必須 reload Tengine,對於單台服務器或者少量服務器這麼操作是可以的,但是對於阿裏雲 CDN 上萬台機器的集群來 reload Tengine 的話,這麼做就不現實了,所以我們也實現了 ssl 密鑰套件列表的動態配置,通過修改 openssl 和 提供 lua ffi 接口的方式,這樣就可以在 ssl_certificate_by_lua_file 階段修改域名級別的密鑰套件列表。

如下圖,可以在阿裏雲 CDN 管理後台管理係統上根據域名來調整密鑰套件。(由於密鑰套件的修改是比較底層的配置,非專業人員配置容易出問題,所以沒有開放到 CDN 用戶控製台。)
https3_7

最後更新:2017-11-09 19:03:34

  上一篇:go  曾經踩坑黨,如今護航忙 | 袋鼠雲的雙11故事之一
  下一篇:go  阿裏雲播放器單擊切換播放/暫停