如何测试SSL客户端库是否正确验证了它所连接的服务器的证书?

时间:2013-06-24 15:50:24

标签: security testing ssl integration-testing

我想确保在SSL证书无效时正确配置客户端库(当前使用Python,Ruby,PHP,Java和.NET)并且失败。 Shmatikov的论文The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software揭示了SSL验证的混乱程度,因此我想彻底测试可能的失败。

根据研究,如果符合以下条件,证书无效:

  • 在激活日期之前使用
  • 在到期日之后使用
  • 已被撤销
  • 证书主机名与站点主机名
  • 不匹配
  • 证书链不包含受信任的证书颁发机构

理想情况下,我认为每个无效案例都会有一个测试用例。为此,我目前正在测试通过HTTP访问的HTTPS网站,这导致我可以在测试中验证失败:

self.assertRaises(SSLHandshakeError, lambda: api.call_to_unmatched_hostname())

这是不完整的(仅涵盖一个案例)并且可能是错误的,所以......

如何测试非浏览器软件是否正确验证了SSL证书?

2 个答案:

答案 0 :(得分:5)

首先,您需要一组SSL证书,其中每个证书只有一个错误。您可以使用openssl命令行工具生成这些内容。当然,您无法使用受信任的根CA对其进行签名。您将需要使用自己的CA.要正确验证,您需要在客户端库中安装CA证书。您可以在Java中执行此操作,例如,使用控制面板。

获得证书后,您可以使用“openssl s_server”工具使用每个证书来提供SSL套接字。我建议你在每个端口放一个证书。

您现在必须使用客户端库连接到端口,并验证您是否收到了正确的错误消息。

我知道默认情况下Python没有证书验证(查看httplib.HTTPSConnection的手册)。但是,m2crypto会进行验证。默认情况下,Java会进行验证。我不了解其他语言。

您可以测试的其他一些案例: 1)通配符主机名。 2)证书链接。我知道旧浏览器中存在一个错误,如果你有一个由根签名的证书A,A可以签署B,B看起来有效。 SSL应该通过在证书上放置标志来阻止这种情况,并且A不会有“can sign”标志。但是,在某些旧浏览器中未对此进行验证。

祝你好运!我很想知道你是怎么过的。

答案 1 :(得分:4)

  • 证书主机名与站点主机名

    不匹配

    这可能是最容易检查的,而失败(失败)肯定有迹象表明出现了问题。众所周知的服务的大多数证书仅使用主机名作为其身份,而不是IP地址。如果您没有要求https://www.google.com/,而是要求https://173.194.67.99/(例如)并且它有效,则会出现问题。

对于其他人,您可能希望生成自己的测试CA.

  • 证书链不包含受信任的证书颁发机构

    您可以使用测试CA(或自签名证书)生成测试证书,但请使用默认系统CA列表进行验证。您的测试客户端应该无法验证该证书。

  • 在激活日期之前使用在到期日后使用

    您可以使用测试CA生成测试证书,notBefore / notAfter日期使当前日期无效。然后,将您的测试CA用作可信CA以进行验证:由于日期的原因,您的测试客户端将无法验证证书。

  • 已被撤销

    这可能是最难设置的,具体取决于撤销的发布方式。再次,使用您自己的测试CA生成一些您已立即撤销的测试证书。 某些工具希望在可信CA集旁边配置一组CRL文件。这需要一些测试本身的设置,但在线设置很少:这可能是最简单的。您还可以设置本地在线吊销存储库,例如使用CRL分发点或OCSP。

PKI测试可能比一般情况下更复杂。完整的测试套件需要对规范有相当好的理解(RFC 5280)。实际上,您可能需要检查所有中间证书的日期,以及链中每个证书的各种属性(例如密钥用法,基本约束等)。

通常,客户端库将验证过程分为两个操作:验证证书是否可信(PKI部分)并验证它是否已发送给您要连接的实体(主机名验证部分)。这肯定是由于这些在不同的文件(分别为RFC 3280/5280和RFC 2818/6125)中指定的事实。

从实际角度来看,使用SSL库时要检查的前两点是:

  • 当您连接到已知主机但具有证书无效的其他标识符(例如其IP地址而不是主机)时会发生什么?
  • 当您连接到您知道无法通过任何默认可信锚点(例如,自签名证书或来自您自己的CA)验证的证书时会发生什么。

两种情况都不会发生连接/验证失败。如果一切正常,除了实现完整的PKI测试套件(需要一定的专业知识)之外,通常需要检查该SSL库的文档以了解如何打开这些验证。

除了错误之外,本文中提到的相当多的问题是由于某些库实现已经假设由用户知道他们在做什么,而他们的大多数用户似乎都有假设图书馆默认正在做正确的事情。 (事实上​​,即使默认情况下库正在做正确的事情,当然也不缺少只想摆脱错误信息的程序员,即使它使他们的应用程序不安全。) 我认为在大多数情况下确保打开验证功能就足够了。

至于一些现有实施的状态:

  • Python:Python 2.x和Python 3.x之间发生了变化。 Python 3.2的ssl模块具有Python 2.7没有的match_hostname方法。 Python 3.2中的urllib.request.urlopen也有一个配置CA文件的选项,它的Python 2.7等价物没有。 (这就是说,如果没有设置,验证将不会发生。我不确定主机名验证。)

  • Java:默认情况下,HttpsUrlConnection的PKI和主机名都会启用验证,但直接使用SSLSocket时不会为主机名启用验证,除非您使用的是Java 7和您已使用SSLParameters(例如)

  • 配置其setEndpointIdentificationAlgorithm("HTTPS")
  • PHP:据我所知,fopen("https://.../")根本不会执行任何验证。