CDN通过openresty库实现ocsp stapling,有效提升客户端回源效率

背景

最近在开发cdn在线加速功能,各个CDN厂商都支持了oscp stapling 功能,所以我们的产品必须也要实现它。实现它的好处就是:可以省掉浏览器和CA机构的服务器校验证书的时间,这样可以提高浏览器的响应速度。

一、什么是ocsp stapling

对于一个可信任的 CA 机构颁发的有效证书,在证书到期之前,只要 CA 没有把其吊销,那么这个证书就是有效可信任的。有时,由于某些特殊原因(比如私钥泄漏,证书信息有误,CA 有漏洞被黑客利用,颁发了其他域名的证书等等),需要吊销某些证书。那浏览器或者客户端如何知道当前使用的证书已经被吊销了呢,通常有两种方式:CRL(Certificate Revocation List,证书吊销列表)和 OCSP(Online Certificate Status Protocol,在线证书状态协议

CRL的方式:CA服务器会维护已吊销的证书列表,浏览器在建立SSL之前先去CA服务器查询下自己所访问的域名证书是否已经被吊销,这种方式的缺点也很明显,比如:这个列表会越来越大,查询时间也会变长。我们这里不过细讨论这种方式。

OCSP的方式:这种方式就是通过OCSP协议去CA服务器查询证书是否被吊销,显然OCSP存在隐私和性能问题。比如:浏览器直接去请求第三方CA(Certificate Authority, 数字证书认证机构),会暴露网站的访客(Let’s Encrypt会知道哪些用户在访问Fundebug);此外,浏览器进行OCSP查询会降低HTTPS性能(访问Fundebug会变慢)。

为了解决OCSP存在的2个问题,就有了OCSP stapling。由网站服务器去进行OCSP查询,缓存查询结果,然后在与浏览器进行TLS连接时返回给浏览器,这样浏览器就不需要再去查询了。这样解决了隐私和性能问题。

二、通过openresty 实现ocsp stapling

1、原理介绍

我们CDN的边缘节点(跟用户最接近的一层)定时去CA网站进行OCSP查询,并把结果缓存在本地,直到过期时再去更新一下。这样,用户来访问时,在TLS握手阶段,我们CDN直接在server hello 中就把OCSP结果下发给用户浏览器了,用户浏览器就不需要再去访问外面的CA服务器了。

2、献上代码

-- Description:动态加载ssl证书 -- Copyright (C) by CRGT, shaoshuli, 2020.05.05 local ssl = require "ngx.ssl" local ocsp = require "ngx.ocsp" local http = require "resty.http" local redis = require "redis_utils" local cjson = require "cjson" -- 清除之前设置的证书和私钥 local ok, err = ssl.clear_certs() if not ok then ngx.log(ngx.ERR, "Failed to clear existing (fallback) certificates.") return ngx.exit(ngx.ERROR) end --[[获取客户端的SNI name, 如果得到的结果为nil,说明客户端没有设置SNI, 这时应该从raw_server_addr method()函数获取serv IP, 进而从dns反向解析出server name. 本功能还待完善。]] local req_host, err = ssl.server_name() ngx.log(ngx.INFO, "req_host:", req_host) if not req_host then ngx.log(ngx.ERR, "Failed to get server name, exit.") --return ngx.exit(ngx.ERROR) end --取出share dict 实例 local shared_data = ngx.shared.shared_data --从redis 中获取ssl公钥和私钥 local function get_my_pem_cert_data() local domain_conf_json = nil --shared_data:delete(req_host) local domain_conf_str, flags = shared_data:get(req_host) --KEY 过期或者不存在,会返回nil if domain_conf_str == nil then --ngx.log(ngx.INFO, "Not found key in share dict, start to set key: ", req_host) local redis_host = "127.0.0.1" local port = 6379 local db_num = 1 local red_handle = redis.connect_redis(redis_host, port, db_num, nil) domain_conf_json = redis.get_meta_from_redis(red_handle, req_host) --ngx.log(ngx.INFO, "domain_conf_json: ", domain_conf_json["domain_name"]) --保存一份到共享内存 local domain_conf_str = cjson.encode(domain_conf_json) local ok,err = shared_data:safe_set(req_host, domain_conf_str, 120) else --已经存在key 了,直接用共享内存的数据 domain_conf_json = cjson.decode(domain_conf_str) --ngx.log(ngx.INFO, "Found key in share dict:", req_host) end --返回ssl证书信息json local ssl_https_data = domain_conf_json["ssl_https_data"] return ssl_https_data end -- 获取证书内容, 从redis 读取 local ssl_https_data = get_my_pem_cert_data() if not ssl_https_data then ngx.log(ngx.ERR, "Failed to get PEM ssl_https: ", err) return end local der_cert_chain = nil local der_priv_key = nil --证书格式有pem 和der两种,针对pem 的证书,我们把它转换成der,方便后续做oscp stapling。 if ssl_https_data['type'] == 'pem' then der_cert_chain, err = ssl.cert_pem_to_der(ssl_https_data['cert_data']) if not der_cert_chain then ngx.log(ngx.ERR, "pem cert transe to der cert chain faild.") return end der_priv_key, err = ssl.priv_key_pem_to_der(ssl_https_data['pkey_data']) if not der_priv_key then ngx.log(ngx.ERR, "Pem key transe to der private key faild.") return end else der_cert_chain = ssl_https_data['cert_data'] der_priv_key = ssl_https_data['pkey_data'] end local ok, err = ssl.set_der_cert(der_cert_chain) if not ok then ngx.log(ngx.ERR, "Set der cert faild.") return end local ok, err = ssl.set_der_priv_key(der_priv_key) if not ok then ngx.log(ngx.ERR, "set der private key faild.") return end ---OSCP更新。共享内存如果不存在或者已过期,则从云端更新oscp response,并update到share dict local req_host_oscp_res = nil --oscp key local req_host_oscp = req_host..'_oscp' req_host_oscp_res = shared_data:get(req_host_oscp) if req_host_oscp_res == nil then local ocsp_url, err = ocsp.get_ocsp_responder_from_der_chain(der_cert_chain) if not ocsp_url then ngx.log(ngx.ERR, "failed to get OCSP responder: ", err) return ngx.exit(ngx.ERROR) end ngx.log(ngx.INFO, "oscp url:", ocsp_url) local ocsp_req, err = ocsp.create_ocsp_request(der_cert_chain) if not ocsp_req then ngx.log(ngx.ERR, "failed to create OCSP request: ", err) return ngx.exit(ngx.ERROR) end local httpc = http.new() httpc:set_timeout(10000) local res, req_err = httpc:request_uri(ocsp_url, { method = "POST", body = ocsp_req, headers = { ["Content-Type"] = "application/ocsp-request", } }) -- 校验 CA 的返回结果 if not res then ngx.log(ngx.ERR, "OCSP responder query failed: ", err) return ngx.exit(ngx.ERROR) end local http_status = res.status if http_status ~= 200 then ngx.log(ngx.ERR, "OCSP responder returns bad HTTP status code ", http_status) return ngx.exit(ngx.ERROR) end req_host_oscp_res = res.body --ngx.log(ngx.INFO, 'req_host_oscp_res:', req_host_oscp_res) --设置ocsp 到 share dict local ok,err = shared_data:safe_set(req_host_oscp, req_host_oscp_res, 600) end --if req_host_oscp_res and #req_host_oscp_res > 0 then if req_host_oscp_res then local ok, err = ocsp.validate_ocsp_response(req_host_oscp_res, der_cert_chain) if not ok then ngx.log(ngx.ERR, "failed to validate OCSP response: ", err) return ngx.exit(ngx.ERROR) end -- 设置当前 SSL 连接的 OCSP stapling ok, err = ocsp.set_ocsp_status_resp(req_host_oscp_res) if not ok then ngx.log(ngx.ERR, "failed to set ocsp status resp: ", err) return ngx.exit(ngx.ERROR) end end

三、抓包验证结果

我们把实现OCSP stapling 和未实现的结果进行下对比:

1、已经实现OCSP stapling的截图如下。从截图我们可以看出,server Hello的时候,服务器就把certifcat Status 下发给用户了。

2、未实现OCSP stapling的截图如下:

原文链接:https://blog.csdn.net/stefan1240/article/details/106000369?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165934461816782391881984%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165934461816782391881984&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~times_rank-29-106000369-null-null.nonecase&utm_term=%E6%90%AD%E5%BB%BAcdn

原创文章,作者:优速盾-小U,如若转载,请注明出处:https://www.cdnb.net/bbs/archives/5959

(0)
上一篇 2022年9月10日
下一篇 2022年9月11日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

优速盾注册领取大礼包www.cdnb.net
/sitemap.xml