使用 RESTful 后端保护 JavaScript 单页应用程序

信息安全 哈希 javascript 休息 单页应用
2021-08-14 07:55:33

我目前正在构建 JavaScript SPA 并一直在研究如何保护它。目前有作为 RESTful API 完全通过 AJAX 进行交互。我们也有与此 API 交互的移动客户端,目前它仅支持基于 SSL 的 HTTP BASIC 身份验证。JavaScript 应用程序也将专门通过 SSL 进行通信,但 BASIC Auth 不会削减它,因为这将涉及在客户端上存储密码(或它的派生词)。最后,SPA 应用程序将是纯 JavaScript 和 HTML,在与 RESTful API 相同的服务器上提供服务,但没有任何服务器端框架。

目标

  • javascript 客户端没有服务器端框架(它只是另一个客户端)。
  • 出于典型原因(可扩展性、容错性、简化部署等),保持 RESTful API 的无状态
  • 任何状态都应由客户端维护。就本问题而言,这意味着登录凭据。
  • 客户端维护的登录状态必须是安全的,并且可以抵抗会话劫持和类似的攻击。

我想出的是基于我对 OAuth 和类似方案(亚马逊等)的研究。

  1. 用户将使用 SSL 上的 HTTP POST 登录。
  2. 服务器将按如下方式计算哈希:

    HMAC(key, userId + ":" + ipAddress + ":" + userAgent + ":" + todaysDateInMilliseconds)

  3. 该令牌将返回给客户端,并与每个后续请求一起提供,以代替用户名和密码。它很可能存储在 localStorage 或 cookie 中。

这安全吗?我选择 userId,ipAddress,todaysDateInMilleseconds 的动机是创建一个仅在今天有效的令牌,但不需要为每个请求查找数据库,并且可以安全地存储在客户端上。我不能相信密钥不会受到损害,因此包含 IP 地址以防止会话劫持。

让我在 StackExchange 上的相关帖子中包含以下链接,因为我认为它解决了我要解决的许多问题:REST 和无状态会话 ID

在这里得到初步反馈后,我决定只使用 IP 地址的前两个八位字节来更好地处理代理和移动客户端后面的客户端。它仍然不完美,但它是一些额外安全性的权衡。

3个回答

令牌提供的服务是服务器将以某种方式将令牌识别为自己的令牌之一。服务器如何验证基于 HMAC 的令牌?通过重新计算它,使用它的秘密 HAMC 密钥和HMAC 操作的数据如果您希望通过用户 ID、密码、IP 和日期计算您的令牌,那么服务器必须知道所有这些信息。但是,您不希望您的服务器存储密码,并且客户端不会在每个请求中将其发送回。那么,您的系统如何工作?

然而,基本的想法是合理的:

  • 用户以您认为合适的任何方式“登录”。
  • 在这种登录时,服务器会发送一个 cookie 值,并与每个后续请求一起发回(这就是 cookie 的作用)。
  • cookie 包含用户 ID、发布日期和值m = HMAC(K, userID || date || IP)
  • 当服务器收到请求时,它会验证 cookie:userID 和 date 来自 cookie 本身,源 IP 是从 Web 服务器层获得的,服务器可以重新计算值m以检查它是否与存储在饼干。

如果服务器有一些(临时)存储空间,您可以用随机会话 ID 替换整个 cookie。实际上,服务器可以记住从随机会话 ID 到用户特定信息(例如他的姓名和 IP 地址)的映射;old session ID 可以自动过期,所以存储空间不会无限增长。上面描述的 cookie 只是一种卸载客户端本身存储的方法。

注意:使用 IP 地址可能意味着一些实际问题。一些客户端在代理后面,甚至是负载平衡代理,因此不仅客户端 IP 地址可能“隐藏”(从服务器上,您会看到代理的地址,而不是客户端的地址),而且您在服务器端获得的 IP 地址可能不规律地移动(如果来自客户端的两个连续请求已经通过代理场中的不同代理)。

有一个更简单的解决方案:

在站点范围内使用 SSL,然后使用框架的标准会话跟踪。

这就是你需要做的。

更详细地说,用户最初通过提供其用户名和密码登录;它被发布到服务器,服务器可以检查它的有效性。如果身份验证成功,您的服务器代码会在会话状态中设置一个标志,以记住用户已成功通过身份验证并记住用户的用户名。所有后续请求都将在同一个会话中进行,因此您可以轻松地对它们进行身份验证并允许它们继续进行。

(更详细:每次收到请求时,检查会话状态以查看用户是否已成功通过身份验证并被授权执行此操作。如果是,则允许请求并执行操作;如果否,则重定向用户到登录页面或显示其他错误消息。)

请注意,这满足您的所有要求。它不需要对每个请求进行数据库查找。它与 RESTful API 兼容。它与单页应用程序兼容。您不需要编写任何花哨的代码:您只需使用框架对会话的现有支持(通常,它使用具有唯一会话 ID 的会话 cookie,以及一些在与该会话 ID 关联的服务器端存储状态的机制) .

作为在站点范围内使用 SSL 的一部分,您应该在会话 ID cookie 上设置安全标志,以保护它不被窃听(这将确保它永远不会通过 HTTP 发送)。您还应该启用 HSTS,以告诉浏览器始终在您的站点上使用 SSL。搜索此站点以获取有关如何在站点范围内部署 SSL 的更多信息。

您不应依赖客户端的 IP 地址是静态的。例如,如果客户端是移动的并且从一个无线网络移动到另一个无线网络,它可能会改变。因此,最好避免将客户端的 IP 地址用于任何事情。

检查这篇文章在 HTML5 Web App 中使用 OAuth2@jandersen 回答很好地解释了在单页应用程序中使用资源所有者密码凭证流。在任何情况下,此应用程序的首选流程应该是隐式授权事实上,@jandersen 对引用帖子的回应是关于调整资源所有者密码凭据以充当接近隐式授权的东西。