我在做一个智力练习,它去了一个我认为不是特别有用的地方,但仍然足够有趣,可以分享。
假设你想给某人一份你以前检索过的网页的副本,你想让他们相信你注册他们的东西在某个时候确实来自原始的Web服务器。
这可能是有用的,例如,对于互联网档案馆。如果我想贡献自己的抓取结果,档案馆如何验证我提交的页面不是伪造的?
请注意,收件人不能自己获取相同的页面,并将其与您提供给他们的内容进行比较。它可能在您检索它时已更改,或者他们可能没有访问该特定文档的权限。
我真正想要的是原始服务器对文档进行数字签名。有一份签名HTTP交换的Internet草案就是这样做的,但是出于本练习的目的,我希望能够在今天部署的任何Web服务器上这样做,而不是要求支持新的协议。
那么,我如何才能让现有的Web服务器为我签署响应呢?
如果我使用的是HTTPS,那么服务器已经在TLS握手期间的CertificateVerify消息中向我发送了一个数字签名,该数字签名可以根据他们的TLS证书进行公开验证。我可以利用这一点吗?
嗯,不完全是。该签名仅涵盖握手的前面部分。TLS对其他所有东西使用各种新的对称密钥,所有这些密钥都来自通过Diffie-Hellman临时密钥交换生成的随机密钥。
TLS握手确实保证只要任何一方都没有泄露任何临时密钥,那么客户端就可以信任与其对话的服务器也拥有与服务器的公共证书相对应的私钥。
因为使用的密码是基于对称密钥的,所以根据定义,客户端和服务器在握手之后共享所有相同的密钥。这意味着任何一端都可以伪造信息,假装来自另一端。但没关系,因为如果我收到你发来的自称是我发来的信息,我就不会上当了。最坏的结果是任意妄为地欺骗自己,而有更简单的方法可以做到这一点。
不过,一旦我试图说服第三方相信这些信息的真实性,这些保证就会烟消云散。
我能想到的唯一方法就是泄露临时密钥,以及完整的加密流。在这一点上,收件人可以验证整个流是由拥有这些密钥的人生成的,并验证服务器提供的证书在对话时是有效的。但他们不能证实我没有伪造所有的申请数据。事实上,他们现在可以自己作假了!
我认为这个问题有一个简单的解决办法,尽管它使解决方案的用处比我希望的要小得多。
让我们介绍一个简单的代理服务器。我会通过这个代理连接到源服务器。代理将记录通过它来回发送的字节,但它不会理解它们:它只会看到加密的TLS流量。在连接结束时,它将在时间戳加上完整的连接日志上给我一个数字签名。我可以检查来自代理的签名是否与我在对话结束时看到的匹配,如果不匹配,则丢弃日志。
(引入此代理并不会改变TLS的安全状况:它必须能够抵御位于客户端和服务器之间的被动或主动攻击者。)。
现在我可以给您签名的连接日志,以及连接过程中使用的临时密钥。
如果您相信代理只对实际通过它的流量生成签名,那么您就有足够的信息来验证我是否与源服务器进行了可信的对话。
嗯,差不多了。如果我能说服代理连接到我控制下的服务器,该服务器就可以完成与原始服务器的握手,然后通过另一个通道与我共享临时密钥,我们可以协作通过代理进行完全伪造的对话,获得原始服务器从未见过的流量的签名。
验证TLS证书的过程包括检查证书中的DNS名称是否与客户端要连接的DNS名称匹配。我们不能使代理完全可信,但如果它尽最大努力确保它连接的服务器与请求的DNS名称相对应,我们可以降低它的不可信赖性。
这至少意味着任何DNSSEC签名都应该包括在代理的流量日志中,这样收件人就可以确信DNSResponse来自同一来源。
就我个人而言,我觉得我非常愿意接受像这样的可信时间戳代理的证据,假设代理是由一个合理的中立方操作的。
如果这对您来说还不够,我怀疑有一种方法可以使用安全多方计算让代理和客户端都参与到会话中,这样就不会在会话期间泄露临时密钥。
但这已经比我预想的要复杂得多,而且与让人们自己从原始服务器获取页面相比,并没有提供太多优势。所以我今天就到此为止了。
不过,仔细阅读TLS1.3规范来解决所有这些问题肯定是一项有趣的练习!