证明客户端应用程序正在使用接收者的公共加密密钥

信息安全 加密 密钥管理
2021-09-11 18:42:32

我正在尝试实现一个网络应用程序(lication),它允许与连接的用户自动共享数据。数据将在客户端上以非对称方式端到端加密。

我的第一个计划是通过应用程序本身共享公钥,但后来我意识到用户需要验证公钥确实来自该用户。现在我告诉他们通过他们已经知道的电子邮件地址共享公钥。

但是仍然存在一个安全漏洞:

用户如何确定我的应用程序使用了收件人的公钥?我仍然可以用我自己的密钥加密消息,拦截它并用接收者的公钥转发它。为了经受安全专家的测试,我只能在特定用户或特定时间这样做。

我认为不可能在同一个应用程序中安全地加密和共享数据。因此,用户必须在另一个应用程序中加密数据并手动将其复制到共享应用程序中。

有解决方法还是归结为信任?


小字

问题“共享加密数据”与此类似,但我无法访问用户的机器,并且答案不质疑公钥共享机制的安全性。

4个回答

为了使用户能够信任系统,她必须能够验证明文在客户端上是否得到安全处理,即与其他用户共享的任何数据在发送到服务器之前都使用该用户的公钥加密,即没有关于明文的信息以某种方式泄露,等等。这取决于用户(或她信任的人)能够检查客户端的源代码,并确保检查的源代码版本实际上对应于客户端正在运行。

是否涉及单个程序或不同作者的多个程序不会对安全性产生任何重大影响(多方之间的勾结很有可能)。但是,如果无法检查源代码,或者将明文发送到服务器,则所有赌注都将失败。即使作者没有恶意使程序不安全,也可能存在使程序不安全的错误。

标准的“网络应用程序”涉及运行从服务器自动下载的 Javascript 代码的用户浏览器。虽然 Javascript 可以直接在客户端执行加密操作,从而避免将明文发送到服务器,但检查源代码是有问题的。用户需要先下载 HTML 和 Javascript 并保存在本地,检查代码,然后运行本地版本,这相当不方便。如果用户直接从 Web 服务器运行代码(可能使用 HTTPS),她不仅必须信任 Web 服务器的运营商,还必须信任存储在浏览器中的数百个 CA 的运营商。

用户如何确定我的应用程序使用了收件人的公钥?我仍然可以用我自己的密钥加密消息,拦截它并用接收者的公钥转发它。为了经受安全专家的测试,我只能在特定用户或特定时间这样做。

在某种程度上,用户将始终需要信任提供他们使用的任何加密软件(以及加密软件使用或与其一起运行的任何库和操作系统组件)的人。

您可以通过发布客户端软件的源代码、邀请安全专家对其进行审查以及鼓励用户从源代码编译软件来在一定程度上缓解此问题。您可以发布源代码(和二进制文件,如果您分发任何)的 SHA-2 哈希,让用户验证他们下载的源代码是官方审查的版本,并鼓励用户共享和比较哈希以确保您的不会向不同的用户发送不同的哈希值。

最终,这些都不会完全阻止您在程序中隐藏恶意代码(即使用户在编译之前亲自审查了代码,编译器本身仍然可能存在Thompson hack),但它确实更有可能如果你尝试它,你会被抓住的。

我认为不可能在同一个应用程序中安全地加密和共享数据。因此,用户必须在另一个应用程序中加密数据并手动将其复制到共享应用程序中。

即使加密和共享功能分开,大多数信任问题仍然存在。

这样做的主要优点是,使用单独的加密应用程序,用户原则上可以在不允许与其他任何通信的沙箱中运行它,除了读取明文和写出密文(反之亦然)反之亦然)。这将使应用程序更难打开一个隐蔽通道将数据传输给第三方——尽管,如果没有对密文进行详细的手动检查,用户很难排除加密文件本身包含的可能性额外的信息或故意的弱点,以允许预期接收者以外的人对其进行解码。此外,在实践中,大多数用户实际上不太可能会在这样的沙箱中运行加密软件。

当然,另一个优势是,将加密和共享代码拆分为单独的应用程序意味着要审查的安全关键代码更少:如果共享应用程序永远不会看到未加密的数据,并且无法控制它的加密方式,那么它真的不需要如此详细审查。此外,如果加密和共享软件是由不同方提供的,那么它们串通破坏组合软件的安全性的可能性就较小(尽管仅仅破坏加密应用程序可能就足够了)。

最终,它确实归结为信任。你可以做各种各样的事情来鼓励信任,并限制你自己破坏信任的能力,但你不能完全消除它。

在你的位置上,为了尽量减少我自己破坏软件的能力,我会做的是:

  • 开源您的应用程序,并公开邀请独立的安全审查。

  • 鼓励用户从源代码编译您的应用程序。

  • 分发您发布的所有源代码(和二进制文件)的 SHA-2 总和,无论是在您的网页上、在发布包中、在邮件列表上还是通过您能想到的任何其他渠道。鼓励您的用户分享和比较来自多个来源的这些校验和,如果它们看起来不匹配,请公开抱怨。

  • 可能在程序中提供基线加密功能,但请明确注意这可能并不完全安全,并鼓励您的用户在此基础上使用额外的加密(他们选择的)。

  • 将任何内置加密代码和代码的任何其他安全关键部分分离到单独的程序中。即使这些帮助程序默认自动调用,为了提供集成的用户体验,用户也应该可以在隔离环境中分别调用它们中的每一个。

  • 使用按需代码分发(例如浏览器内的 JavaScript)或任何类型的自动更新机制:这些使得向特定用户交付受损代码变得非常容易(例如以错误修复更新为幌子)。唉,避免这些机制也使得向用户分发合法的错误修复变得更加困难,所以这里有一个棘手的安全权衡。

    您可以在这里做的最好的事情是 a) 通过用户已经信任的渠道分发更新,并且最好至少涉及一些外部审查,例如 Linux 上的系统包管理器,或者 b) 轮询更新(通过 SSL ),但不要自动下载它们;鼓励您的用户在安装更新之前通过外部渠道验证确实存在合法更新(并执行上述 SHA-2 总和检查)。

  • 接受这一点,无论我做什么,软件总是有可能被破坏。还要接受,在实践中,大多数用户不会关心,并且会满足于信任你,不管他们是否应该。

这确实与两个对等点之间的端到端加密无关。它是“全知服务器”的经典模型,它使数据可供客户端使用。您的用户需要完全信任您,因为模型的安全性完全取决于您的诚实和能力。

整个非对称加密在您的场景中并没有真正发挥作用。如果您的服务器受到攻击,攻击者很可能会在纯文本数据进入时获取它。唯一的优点是在数据被加密并位于您的服务器上之后很难读取数据。

端到端加密和“全知服务器”是两个相互排斥的模型。你不能同时拥有两者。如果您想管理用户数据,那么它不是端到端加密。如果你想要端到端加密,你就无法管理用户数据。您的服务基本上仅限于经典文件托管和密钥分发(必须由用户亲自验证,就像使用任何其他密钥服务器一样)。

用户如何确定我的应用程序使用了收件人的公钥?

如果您想信任用户的公钥,您需要有信心直接收到它,或者获得第三方保证。您可以让CA签署用户的公钥,也可以让您信任的其他密钥签署密钥(信任网络)。

如果您的服务器只是为我提供了一个未签名的公钥或证书,我就没有理由相信或相信它。您需要直接获取它或依赖第三方信任。

我仍然可以用我自己的密钥加密消息,拦截它并用接收者的公钥转发它。

如果加密发生在您的服务器上,您可能是中间人,您也可以以纯文本形式拦截它,因为用户需要在您的服务器进行加密之前信任您的服务器。

如果您在做这个客户端,并且想要确保发件人是合法的,您将使用发件人的私钥对消息进行数字签名

如果您在客户端使用签名/验证证书进行所有签名和加密,那么您的服务器将只上传加密和签名的文档。做所有这些都是浏览器应用程序会很痛苦,有些应用程序需要插件/附加组件才能使这更容易。而且,用户必须相信您没有运行 JavaScript 才能在浏览器中拦截他们的命令(信任桌面应用程序也是如此)

无论如何,如果在上传之前一切都在客户端完成,如果您没有收件人的私钥,您将无法解密它。如果您发送您的公钥而不是收件人的公钥,最终用户应该能够检测到这一点,因为该密钥不会被有效地确认为属于收件人。