WordPress + Nginx 客户端证书验证的坑

我始终在关注各种能够提升访客访问本站点体验以及安全性的优化措施。继去年底将博客接入 Cloudflare 后,在最近一段时间本站又将博客源服务器进行了扩容升级。借着本次升级,我们配置了 Nginx 中一项被称为客户端证书验证的安全措施,随之踩了不少坑,遂撰本文以分享,希望能帮助更多人。

tl;dr不要为 WordPress 站点开启源服务器上的客户端证书验证功能

客户端证书验证

互联网站的访问者依赖服务器提供的具备信任链的证书验证服务器的身份,并保证连接的安全性。但这种验证能力不止在服务器端可以提供证书以证明其身份,在 TLS(传输层安全)协议 1.2 版本的 RFC 文档的 7.4.6 小节中,客户端也能在请求时提供相应证书,以向服务器端证明访客的身份。这通常用于访问机密数据,或限制服务器字段的访客身份时使用。

Cloudflare 提供了请求源服务器时夹带 Cloudflare 客户端证书的选项,当源服务器中进行相应配置时,就可以做到仅允许来自 Cloudflare 的流量访问到源服务器。而其他请求到源服务器的未经验证的请求,则会被网络服务器直接拒绝,以提高安全性。

Apache 与 Nginx 网络服务器均提供了用于进行客户端证书验证的配置功能,可以参阅由 Cloudflare 整理的这篇文章

一些问题

配置好客户端证书验证后,我注意到当尝试在管理后台编辑 WordPress 文章或新创建文章,以及自定义网页主题时,访问经常会出现超时的情况(Cloudflare 524 错误),这表明 Cloudflare 无法在 100 秒(预设时间)内接受到来自源服务器的响应,从而导致错误。我尝试通过多种方法来诊断和定位问题,包括开启 Nginx 服务器的调试(debug)级别的日志,以及通过 WordPress 的执行日志进行诊断,但仍未能发现问题。

通过不断试错,我注意到当禁用网络服务器的客户端证书验证功能时,一切都恢复了正常:

...
    # ssl_verify_client on;
...

我上网搜寻了一些相关资料,猜测问题可能是由于 WordPress 在访问自定义主题编辑器或文章区块编辑器时,会通过类似内部请求的方式向本地网络服务器进行 HTTP 请求,而这些请求并未附带客户端证书,导致请求失败。这些仅仅是猜想,截止撰写本文时,我未能通过任何日志获得证据来支持这一点。

不过从观察到的现象来看,当我们临时禁用网络服务器上的客户端证书验证功能,成功加载上述页面后,将验证功能重新打开,上述页面在一定时间(看起来不超过 24 小时)内也能正常加载,所以我也一度怀疑问题可能与 wp-cron 定时任务有关,不过还没有仔细整理这一部分的逻辑。

弊端

我们在此部分更多地讨论与禁用源服务器网络服务器上的客户端证书验证会产生何弊端。首先对于公众可访问的站点来说,客户端证书验证功能通常在访客通过类似 Cloudflare 这样的代理服务器访问网站时比较有用。因为如果访客直接与源服务器建立链接,要求每位访客提供客户端证书是没有意义的。然而,如果攻击者得知源服务器的地址,通过直接向源服务器指定域名进行请求(如下例代码)的方式也是可以访问到对应站点的,这会造成潜在的安全问题:

curl https://DOMAIN.EXAMPLE --resolve 'DOMAIN.EXAMPLE:443:192.0.2.17'

因为这样一来,流量不会经过 Cloudflare 防火墙和 DDoS 防护的预先过滤,会造成潜在的安全问题。但如果源服务器地址不被泄漏,该问题不是十分严重。

由于我仍未完全了解问题的发生原因,所以如果哪位读者有更多信息可以分享,或者希望针对此主题进行更多讨论,欢迎通过博客信息页面中提到的联系方式联系我,不胜感激!