如何检测与清理不可信的 CA 证书

·6 分钟阅读时长

如何检测与清理系统或环境中不安全、不可信的 CA 证书等?常用系统和环境的操作指引见下文。本篇是“自签名证书与安全”系列之三。

为什么有必要检测和清理系统里的 CA 证书?CA 证书包括根证书和中间证书。客户端访问服务时依赖可信任的 CA 证书来验证服务端身份以及对传输加密、不被窃听和篡改等。如果系统中包含不可信的 CA 证书,则安全三要素中的保密性和完整性得不到保障。

哪些证书不可信?

要清理不可信的证书,首先要解决的问题是如何判断证书不可信。 证书可信与否,一般来说取决于以下几点:

  1. 是否由“权威”、“可信任”的机构所颁发;
    在通常的安全级别要求之下,权威、可信的机构可以认定为:一是操作系统厂商,如微软、Redhat、Apple 等;二是软件或平台供应商,如 Mozilla (for Firefox)、Oracle (for Java Runtime Environment) 等;三是公司或团队内部负责自签名根 CA 管理的部门或个人,这主要是内部环境使用。
  2. 是否在有效期内,且没有被吊销;
    证书不在有效期就不说了,即使在有效期内,如果被吊销了也是不安全的。某些原本权威可信的机构(如 Comodo)发生了“信任”危机(被黑客入侵)后,根证书被吊销了。
  3. 没有被篡改;
    哈希指纹正确无误,带交叉验证的能通过验证。

本文的前提是信任主流操作系统厂商和 Mozilla 等软件提供商。更高的安全要求不在本文讨论范围,有兴趣可以参考论文 Tracing your roots: exploring the TLS trust anchor ecosystem 1 的分析。

如何检测?

对于公司或组织,可以考察一下已经部署的安全软件(EDR 或 HIDS 等)是不是带有相关功能。例如

  • 例一,开源的 osquery文档 中可以看到支持 macOS 和 Windows 系统的证书信息收集,可以在统一的平台上部署检测策略。
  • 例二,Security Onionplaybook 功能中包含检测 certutil.exe\certmgr.exe(windows) 和 update-ca-certificates update-ca-trust 等进程执行的功能,可以对证书安装发出警告。

如果没有上述安全软件的支持(例如 osquery 文档未指明对 Linux 下证书的检测),该如何检测系统已安装根证书的安全性? 最简单的逻辑就是与最新的可信证书列表进行比对,如果 既不在可信供应商的可信证书列表中,也不是自主颁发的自签名根证书,则不可信

最新的可信证书列表在哪里?Github 上有个很好的项目可以参考:https://github.com/nabla-c0d3/trust_stores_observatory ,分析 store_fetcher 目录下的源码中可以找到可信证书来源。

  • Windows: Microsft 在这里 公布了最新列表的地址 。(另外也可以从 Windows 日常更新根证书的地址下载 stl 文件)
  • macOS\iOS: Apple 在 https://support.apple.com/en-us/HT209143 提供了表格
  • Firefox: 直接从 Mozilla 开源代码库中获取
  • JDK,区分 Oracle 版和 OpenJDK,分别从最新安装包里解出 cacerts 文件和 blocked.certs 文件之后再解析

图省事的话可以直接获取该仓库的 yaml 文件 。文件中用于比对的关键信息是 fingerprint,即证书的 SHA256 哈希值。

有了最新可信证书列表,接下来就比对。通过查询本地已安装的所有根证书的 SHA256 哈希,如果在前述列表中不存在,则需要人工核实是不是不安全的证书。确认为不可信的证书,就可以备份之后删除之。

Windows

根证书存储位置

Windows 系统当前安装的所有证书可以通过证书管理控制台查看。新版系统使用 certlm.msc 和 certmgr.msc 分别管理“本地计算机”和“当前用户”的证书。

证书管理

检测方法

Windows 系统的最新根证书列表总是可以从 http://ctldl.windowsupdate.com/msdownload/update/v3/static/trustedr/en/authrootstl.cab 下载2。解压后的文件格式并不能直接查看,需要用 certutil.exe 命令行工具操作。

作为参考,可以看看 Github 的这个项目 。其中两个 Powershell 脚本,看源码就能明白它的思路:

  • get-microsoftrootcalist.ps1 的作用是下载并导出所有最新根证书的哈希值到一个文件文件;
  • verify-trustedrootca.ps1 的作用是拿当前系统中安装的系统以及用户信任的根证书,与前面的文件中的哈希值比较,不存在于前面文件中的证书,会记录在一个 csv 文件以及系统的事件日志中。
  • 因为 Windows 存储证书用的路径是 SHA1,脚本为简单起见哈希比较用的也是是 SHA1,不是 SHA256。

但是 get-microsoftrootcalist.ps1 是通过提取 certutil.exe 的 dump 输出来获取信息,在中文环境下运行会有问题。我们来看另一个工具:

sigcheck

微软官方就有一个命令行工具能完成与上一节类似的检测。

sigcheck.exe -tv
sigcheck.exe -tuv

注意:命令输出中显示的证书只表示当前不在微软最新的可信根证书列表中,不代表你不能信任它。

例如我的系统中会显示有 XBL Server IPsec Issuing CA 证书,这是用于 Xbox Live 网络服务器和客户端验证用途的证书;另外,用户或公司自签名的证书也会显示在输出中,需要自行核实指纹(Thumbprint)正确与否(有没有被篡改)。

删除证书

  • 方法一:手动删除
    • 打开 certlm.msc 和 certmgr.msc 里,在“受信任的根证书颁发机构”和“第三方根证书颁发机构”里找到对应的根证书,右键菜单删除。
  • 方法二:使用以下 Powershell 脚本。(功能接口参考3)
    • 脚本文件命名为 remove-rootca.ps1
    • 在 Powershell 命令行窗口中执行 . .\remove-rootca.ps1 -Thumbprint 59973EC928CD924B5E4DC2ABAC727301ACCFDCAC 删除指定的根证书
    • 系统存储中的证书需要在管理员权限下执行,否则提示拒绝访问
    param(
    [string]$Thumbprint=$(throw "Parameter missing: -Thumbprint Thumbprint")
    )
    
    $CAStores="cert:\currentuser\root", "cert:\currentuser\authroot", "cert:\localmachine\root", "cert:\localmachine\authroot"
    
    $exist = 0
    foreach ($store in $CAStores) {
        $location = $store+"\"+$Thumbprint
        if(Test-Path -Path $location) {
            $exist = 1
            Remove-Item  -Path $location -Force
        }    
    }
    if ($exist -eq 0) {
        Write-Host "Thumbprint does not exist."
    }
    

Linux & macOS

根证书存储位置

Linux 各发行版下一般都可以安装一个名为 ca-certificates 的包,包中安装的根证书实际上来自于 Mozilla NSS 项目4。根证书存储路径在不同发行版下有差别,但这些根证书都会汇总到一个单独的 PEM 格式文件中(可能扩展名为 crt)。对应的具体路径可以参考如何安装和信任自签名证书 中的表格。

另外,各 Linux 发行版的根证书包更新时间各不相同,并且通常都滞后于 Mozilla NSS。所以与 Mozilla 最新数据比较的结果通常都存在差异,其中重点要关注 NSS 已删除的证书。Mozilla 提供了已删除证书的时间及原因5,可以在“Removal Bug No. or Date”列中找到相关记录。

macOS 的证书存储在 keychain 中,可以在“钥匙串访问”应用中查看。

检测工具:ctlcheck

搜索了一圈,没有找到现成的对 Linux 或 macOS 下根证书可信列表进行检查的工具。所以动手写了一个命令行工具 ctlcheck

ctlcheck

  • 功能:
    • 当前支持 Linux 和 macOS 下的系统根证书分别与 Mozilla 和 Apple 的最新列表比较并显示结果。
      • Trust 指时在系统已安装的证书 和 官方最新列表中存在
      • Remval 指系统已安装,但官方列表中已删除或禁用
      • Unknown 指系统中安装,但在官方列表中找不到
    • Allow 指白名单。可以在配置文件中把需要的自签名证书或公司证书设置为允许
  • 安装:
    • 暂时只提供通过 go 编译安装的命令行,等有空了再弄自动打包成二进制。
    • GOBIN="$(pwd)" go install github.com/canstand/ctlcheck@latest
  • 使用:
    • 默认联网获取最新可信列表并输出当前系统检查结果。
    • -save 参数可以保存在线获取的列表,下次用 -offline 就不需要联网了。

如何清理?

不需要或不信任的证书可以从系统中删除。删除系统存储中的证书需要管理员权限。

  • Windows 系统下前面提供了删除证书 的方法
  • Linux 下需要手动删除。另外 Redhat/CentOS 也可以参考知识库文章 完全复位系统证书列表。
  • macOS 下可以在“钥匙串访问”中手动删除。

转载许可声明

CC-BY 4.0本作品采用知识共享-署名-相同方式共享 4.0 国际许可协议 进行许可,转载时请注明原文链接。