首頁>技術>

隨著 https 全線推廣,證書私鑰散落在各處,不便於管理, 而且一旦伺服器被駭客 takeover,或者再次出現像 heartbleed 一樣的漏洞,證書私鑰就面臨著被洩漏的風險,所以我們需要探索一種能夠保護伺服器證書私鑰的技術方案。

0x01 ngx.ssl

第一種方案將證書放置於遠端伺服器,定時將證書私鑰從 keyserver 拉到 webserver 記憶體中進行 tls sign/decrypt 操作。

> webserver: https 連線 webserver ->> keyserver: 請求獲取連結對應證書及私鑰 keyserver ->> webserver: 返回證書及私鑰 webserver ->> webserver: 設定證書及私鑰,並進行運算 webserver ->> client: 返回 response -->

具體實現可以使用 OpenResty ngx.ssl 模組中 ssl.set_der_cert 和 ssl.set_der_priv_key 為當前連線動態設定證書及私鑰:

-- modify from https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl.md#synopsisssl_certificate_by_lua_block {    local ssl = require "ngx.ssl"    -- 清除當前連線證書    local ok, err = ssl.clear_certs()    if not ok then        return ngx.exit(ngx.ERROR)    end    -- 自定義函式 my_load_certificate_chain 載入遠端證書    local pem_cert_chain = assert(my_load_certificate_chain())    local der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain)    if not der_cert_chain then        return ngx.exit(ngx.ERROR)    end    -- 為當前連線設定證書    local ok, err = ssl.set_der_cert(der_cert_chain)    if not ok then        return ngx.exit(ngx.ERROR)    end    -- 自定義函式 my_load_private_key 載入遠端證書私鑰    local pem_pkey = assert(my_load_private_key())    local der_pkey, err = ssl.priv_key_pem_to_der(pem_pkey)    if not der_pkey then        return ngx.exit(ngx.ERROR)    end    -- 為當前連線設定證書私鑰    local ok, err = ssl.set_der_priv_key(der_pkey)    if not ok then        return ngx.exit(ngx.ERROR)    end}

這種方案的優點是:

實現簡單證書私鑰不落盤證書私鑰能夠在統一的節點進行管理

缺點只有一個,伺服器被 takeover 或者再一次發生 heartbleed 的時候,證書還是有可能會被洩漏

0x02 keyless

第二種方案將證書放置於遠端伺服器,將 tls 連結中需要 sign/decrypt 的引數提供給 keyserver,讓 keyserver 進行 sign/decrypt 操作。

> webserver: https 連線 webserver ->> keyserver: 提供 tls handshake 中相關的 params 資訊 keyserver ->> keyserver: 使用對應的私鑰對 params 進行 sign/decrypt keyserver ->> webserver: 返回計算後的結果 webserver ->> client: 返回 response -->

提出這種方案的是 Cloudflare: Keyless SSL: The Nitty Gritty Technical Details ,主要是為了給那些不願意提供自己證書的客戶使用,那 keyless 適用於甲方內部嗎?cloudflare 內部也在嘗試: Going Keyless Everywhere ,主要是為了把 web 伺服器和證書進行分離,防止伺服器被 takeover 後證書洩漏。在目前(2019-11)為止,cloudflare 在 TLS 1.3 流量和 Spectrum 業務上使用了 keyless 。

在實現上,我們先用比較流行的 Nginx + OpenSSL 做分析,那麼目前有沒有其他將 TLS 中非對稱加解密的操作從 OpenSSL 中剝離出來的方案呢?有,那就是 “intel QAT 非同步加速方案”。

intel QAT 主要依靠 OpenSSL 的兩個特性 OpenSSL ASYNC 和 OpenSSL Engine 來搭配實現。

OpenSSL ASYNC 能夠在 async_job 執行過程中,在等待加速卡結果的時候,將 cpu 讓出去,在沒啟用 async 模式時,呼叫 openssl 函式是阻塞操作:

開啟之後則是非阻塞的呼叫:

OpenSSL Engine 則是提供了自定義註冊加解密的方法,不使用 OpenSSL 自帶的加解密庫,轉而自己實現或者呼叫第三方的加解密庫

我們再看一下 async_job 執行流:

那我們是不是可以和 intel QAT 一樣在 Nginx 啟用 OpenSSL ASYNC 模式,然後再利用 OpenSSL Engine 呼叫 keyless server 呢? 如果你的 nginx server 只有一份證書,那沒問題,但不同的 server_name 使用不一樣的證書的時候,可能就不行了,我們先看一下 EVP_CIPHER 結構:

struct evp_cipher_st {    int nid;    // ...    /* init key */    int (*init) (EVP_CIPHER_CTX *ctx, const unsigned char *key,                 const unsigned char *iv, int enc);    /* encrypt/decrypt data */    int (*do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out,                      const unsigned char *in, size_t inl);    // ....} /* EVP_CIPHER */ ;

再看一下 EVP_CIPHER_CTX 結構:

struct evp_cipher_ctx_st {    const EVP_CIPHER *cipher;    ENGINE *engine;             /* functional reference if 'cipher' is                                 * ENGINE-provided */    int encrypt;                /* encrypt or decrypt */    int buf_len;                /* number we have left */    unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */    unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */    unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */    int num;                    /* used by cfb/ofb/ctr mode */    /* FIXME: Should this even exist? It appears unused */    void *app_data;             /* application stuff */    int key_len;                /* May change for variable length cipher */    unsigned long flags;        /* Various flags */    void *cipher_data;          /* per EVP data */    int final_used;    int block_mask;    unsigned char final[EVP_MAX_BLOCK_LENGTH]; /* possible final block */    /* Provider ctx */    void *provctx;    EVP_CIPHER *fetched_cipher;} /* EVP_CIPHER_CTX */ ;

我沒有發現有可以和 SSL_CTX 關聯的欄位,也就是說在實際的加解密操作函式中是沒法獲取當前 handshake 相關資訊, 也就沒有辦法告訴 keyless server 該使用哪一個私鑰去做 sign/decrypt 操作,其實還是有變相解決的方法: 提供一個 fake 私鑰給 openssl engine,轉而提供給 keyless server,從而使用 fake 私鑰找到 true 私鑰。

Cloudflare 應該不是使用這種方式去實現,因為 Cloudflare 釋出技術細節的時間是 2014-09 ,然而 2015-11 時 OpenSSL才支援 async mode。

考慮到對 nginx + openssl 進行修改比較複雜,我選擇了 nginx 同類產品 bfe 進行修改。 因為 Go 的 crypto/tls 模組相比 openssl 模組要容易修改的多,而 bfe 的 bfe_tls 模組就是拉取官方 crypto/tls 模組進行二次修改的, 除了程式碼落後官方程式碼好幾年之外,也沒什麼太大的缺點。將 keyless 模組整合進去,需要拉去官方几個 commit 的程式碼, 而且 bfe 提供了設定第三方 cert 提供策略 bfe_tls.SetTlsMultiCertificate 可以很方便的實現整個 keyless 方案。

我的實現: BFE with keyless ,具體安裝以及測試資訊都在 README 中。

這種方案的優點是:

即便 webserver 被 takeover,也不會洩漏證書私鑰證書私鑰能夠做到統一管理

缺點是:

實現複雜gokeyless license 問題 (不確定到底能不能公司內部使用,不過實現比較簡單)0x03 Delegated Credentials for TLS

Keyless 方案最大的問題是每個 client 的新連線都需要 Web Server 往 Keyless Server 傳送 sign/decrypt 請求, 在甲方內部 Web Server 和 Keyless Server 一般都在同一機房,這種情況還能接受。但是像 Cloudflare 這樣的 CDN 廠商, CDN Server 和 Keyless Server 相隔可能十萬八千里的,這問題就嚴重了,所以 Cloudflare / Facebook / Mozilla 提出了 RFC: Delegated Credential for TLS

> webserver: https 連線 loop 每小時 keyserver ->> webserver: 推送 delegated credential end webserver ->> webserver: 設定證書及私鑰,並進行運算 webserver ->> client: 返回 response -->

簡單描述就是由真正的證書生成 Delegated Credential (失效時間幾小時),然後將 Delegated Credential 提供給 webserver 當作正常證書進行使用。在 keyless 場景就是 keyless server 生成 Delegated Credential,而後將 Delegated Credential 推送給 CDN 使用 (和正常的證書一樣使用)

嚴格來說 Delegated Credential 並不是一種 keyless 方案,但也能很好的對私鑰進行保護,我也不確定 cloudflare 得到 Delegated Credential 後還是不是繼續走方案二這一套,但如果在小米內部,則可以不使用方案二,直接將 Delegated Credential 當正常證書使用,反正證書失效時間只有幾小時。可惜的是方案三的 RFC 還沒定下來,而且只支援 TLS1.3 而且現在只有 nightly firefox 支援,所以方案三目前來說完全不可行。

0x04 總結

第一種方案沒有達到要求,第三種方案目前還沒落地,目前只有第二種方案能符合要求,TLS Keyless 方案小米內部也在不斷的嘗試中,將來也有可能會推廣到其他非 https 的場景中, 如果你對這個專案感興趣,或者對漏洞掃描器、WAF、IoT 自動化安全與評估、日誌審計等安全專案感興趣,那麼歡迎你加入我們

0x05 引用

https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/https://blog.cloudflare.com/zh/going-keyless-everywhere-zh/https://blog.cloudflare.com/keyless-delegation/https://engineering.fb.com/security/delegated-credentials/https://blog.mozilla.org/security/2019/11/01/validating-delegated-credentials-for-tls-in-firefox/https://tools.ietf.org/html/draft-ietf-tls-subcerts-07https://github.com/fate0/bfehttps://github.com/baidu/bfehttps://01.org/sites/default/files/downloads/intelr-quickassist-technology/intelquickassisttechnologyopensslperformance.pdf

作者:fatezero

出處:http://blog.fatezero.org/2020/05/29/tls-keyless

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • SSL 通訊雙方如何判斷對方採用了國密?