在 HTML5 SPA 中进行负载签名的有效方法

Efficient way to do payload signing in HTML5 SPA

我正在寻求实现一些 高效(即具有良好性能)的逻辑,以在我们的 Web 应用程序中进行负载签名。 HTML5 客户端的目标是保证接收到的有效负载的内容确实是由我们的后端生成的内容。

我们不想使用共享盐生成负载哈希,因为用户可以轻松打开 HTML5 源并找到盐短语。

我们现在已经实施了 RSA 签名,我们的后端使用它的私钥添加有效负载签名,我们的 HTML5 客户端使用其内置的 Public 密钥验证它。然而,签名生成过程需要 250 毫秒(对于相对较小的有效负载),并且由于签名请求的性质,这个时间量是不可接受的。

唯一的其他想法是在每次客户端初始化其与后端的会话时在运行时生成共享机密。然而,秘密不能以明文形式发送,所以看来我们将不得不实施 Diffie-Hellman 交换机制,如果可能的话我们希望避免这种情况或使用现有库实现自动化。

请记住,由于我们销售产品的性质,保密和加密需要在 应用层 完成。我们不打算加密我们的流量,这是我们的客户可能会或可能不会实施的事情(因为它是一个内部网应用程序)。但是,我们必须避免向他们公开与我们的许可检查机制等相关的内容。后端 不是 基于云,并且 不是 由我们控制,而是安装在客户的机器上。

前端是 Java 脚本,后端是 Java。

请注意,Diffie-Hellman 交换机制防止 MITM 攻击,因此不加密流量意味着您需要对来自服务器的 DH 数据进行身份验证。这就是为什么使用基于 DH 的密码套件的 Web 服务器使用其服务器证书的私钥对通过网络发送的 DH 元素进行签名,以便客户端检查这些元素是否真的来自他想要连接的服务器。这些元素是 public 但需要签名。

你所说的 "payload hash generation with shared salt" 是一个密钥散列消息认证码,所以它是基于一个共享的秘密,正如你所注意到的,既然你不想使用这种机制,那就意味着你不信任客户。因此,您必须使用非对称加密来签署您的有效负载。

使用非对称算法签署服务器负载意味着您​​首先需要让服务器与客户端共享一个 public 密钥。由于您没有对客户端和服务器之间的数据进行加密,因此您需要在客户端源代码中部署服务器 public 密钥。

你说的是签名生成过程,但是客户端的签名校验过程在你的情况下也很重要,因为用户等待结果的总时间是签名时间的总和以及检查签名的时间(此外,如果要签名的数据不是动态生成的,则通常可以在服务器上预期签名,但永远无法预期验证)。所以你需要一种在客户端检查签名的快速方法。首先,签署一个散列,而不是整个有效载荷。然后在客户端选择您的开发环境中可用的最快的非对称签名算法。请注意,对于对应于相同安全级别的相应密钥长度,检查 RSA 签名比检查 DSA 或 ECDSA 签名更快。所以你应该继续使用 RSA。

这一行之前的所有内容可能对您没有太大帮助!现在有一种使用 RSA 来签署和验证签名来提高性能的方法,这种方法与 SSL/TLS 在从同一服务器下载多个页面或其他对象时提高浏览器性能的实现方式相同:使用会话缓存。您与一个特定用户共享特定会话的公共秘密。切勿将此公共秘密用于其他会话。当用户第一次连接时,只使用 RSA 一次,交换一个临时的共享秘密或交换 DH material 来创建这个共享秘密。然后,每次服务器需要签署一个对象时,它都会用这个特定的秘密创建一个密钥哈希消息认证代码。因此,如果用户找到了秘密,例如使用他的浏览器的调试模式,这不是问题:这个秘密只是帮助他知道来自服务器的东西没有被改变。所以用户不能使用这个秘密来改变服务器和其他用户之间的数据交换。

我们最终在客户端和服务器端都使用了 TweetNaCl。该库提供了一种简单快捷的方式来进行类似 DH 的共享秘密交换,而无需通过自定义实现。使用临时共享密钥,我们可以轻松地为我们的有效负载生成哈希而不是签名,从 250 毫秒减少到 10 微秒。此外,RSA 签署初始 DH 交换很重要,也是我们唯一使用 RSA 的地方。

请阅读@AlexandreFenyo 的回答,了解如何通常 处理此类情况的正确理论。