1

我在 Express 中有一个服务器,它向 Web 应用程序公开一些 API。我正在寻找一种管理最终用户和第 3 方身份验证的好方法。

现在,当用户使用电子邮件和密码注册时,服务器会生成与该用户关联的 JWT。

function createToken(user, role) {
    var usr = {
        role: role,      // admin | customer | shop
        email: user.email,
        name: user.name
    };

    var expires = (Date.now() / 1000) + 60 * 60 * 24 * 365; // 1 year
    var nbf = Date.now() / 1000;
    usr['nbf'] = nbf;
    usr['exp'] = expires;
    var token = jwts.encode(usr, process.env.SECRET);
    return token;
}

当 Web 客户端收到该令牌时,它会将令牌存储在 cookie/web_storage 中,并将其用于对服务器的每个 API 调用以及自动登录。令牌还包含role,因此当服务器收到请求时,它知道该用户/角色是否可以访问所请求的路由/资源。

function checkToken(token, api_name) {
    // verifies secret and checks exp
    jwt.verify(token, process.env.SECRET,
        function (err, decoded) {
            if (err) { throw { msg: "token expired or not authenticated", code: errors.ERR_NOT_AUTH }; }
            else {
                var role = decoded['role'];
                return does_role_can_access_api(role, api_name); // true or false             
            }
    });
}

现在一些第 3 方想要访问我的一些API。我想创建一个 Express-Gateway 来为想要使用我的服务器的应用程序制作 api-keys,并为单个用户保留现有的 JWT 身份验证。

所以我会有

 |----------------|
 |   my Web-App   |
 |----------------|----> |------------|            |------------|
                         |   Express  |            | my Server  |
                         |   Gateway  |----------> |    APIs    |
 |----------------|----> |------------|            |------------|
 |    3rd party   |
 |----------------|
  • 我的 Web-App 应该访问所有 API,因为我的 Web-App 被admins(像我一样)和我们的用户(customersshops)使用。
  • 3rd 方应用程序应仅访问某些 API,因为它们将仅是customersshops.

所以我想做这样的事情:

 |----------------|
 |   my Web-App   |
 |     scopes:    |
 | [admin, user]  |
 |                |
 |----------------|----> |------------|            |------------|
                         |   Express  |            | my Server  |
                         |   Gateway  |----------> |    APIs    |
 |----------------|----> |------------|            |------------|
 |    3rd party   |
 |    scopes:     |
 |     [user]     |
 |----------------|

最后,我的 Web 应用程序将有一个包含所有范围的 ApiKey,而第 3 方 ApiKeys 将只有user范围,所以我可以过滤路由。单个真实用户,无论使用何种 App,都将使用 JWT 令牌登录并发出请求。

因此,每个请求都会有一个 ApiKey(基于所使用的应用程序)和一个 JWT 令牌(用于识别用户):

  • ApiKey 将由第 3 方服务器添加到标头中,
  • JWT 令牌将由用户浏览器的 web_storage (检索并)添加到标头中。

听起来不错?

4

1 回答 1

1

首先祝贺您在开发安全应用程序方面所做的所有努力,因为并不是每天我们都能看到开发人员走得那么远。

澄清可能的误解

在深入探讨您的问题之前,我想首先澄清一个误解,即开发人员通常会在访问他们的后端的内容和访问者方面存在误解。这在本文中有详细讨论我们可以在其中阅读:

向 API 服务器发出请求的内容是什么。它真的是您的移动应用程序的真实实例,还是机器人、自动脚本或攻击者使用 Postman 之类的工具手动绕过您的 API 服务器?

是移动应用程序的用户,我们可以通过多种方式进行身份验证、授权和识别,例如使用 OpenID Connect 或 OAUTH2 流。

虽然本文是在移动应用程序的上下文中,但为了理解访问 API 服务器的对象和访问者之间的区别,可以将对的引用mobile app替换为web app. 如果您对盗窃有疑问,请阅读链接文章的部分,该部分还包括有助于理解这一点的图形。

你的问题

现在,当用户使用电子邮件和密码注册时,服务器会生成与该用户关联的 JWT。

var expires = (Date.now() / 1000) + 60 * 60 * 24 * 365; // 1 year

我的 Web 应用程序应该访问所有 API,因为我的 Web 应用程序由管理员(如我)和我们的用户(客户和商店)使用。

这是一种很长的身份验证令牌,特别是当您说 API 由管理员访问时,但即使对于普通用户来说也太长了。

根据您的用例,我建议它们在分钟范围内,因此我建议您切换到使用刷新令牌,这将使访问令牌保持短暂,而刷新令牌可以长期存在,但在小时范围内,而不是几天、数周或数年。

刷新 Token 流程示例:

用于缩短令牌生命周期的图形

来源:移动 API 安全技术 - 第 2 部分

注意:虽然上图属于在移动 API 上下文中编写的一系列文章,但它们有很多信息也适用于为 Web 应用程序和第三方客户端提供服务的 API。

通过使用这种方法,短期访问令牌失败的客户端将需要通过发送刷新令牌来请求新的令牌以获得新的访问令牌。

这里重要的一点是刷新令牌不应该发送到浏览器,只能发送访问令牌,因此您的第三方客户端必须非常清楚这一点,这样就不会尝试直接从 javascript 访问您的 API,相反,他们应该将其委托给他们的后端。

API 密钥和 JWT

最后,我的 Web 应用程序将拥有一个包含所有范围的 ApiKey,而第 3 方 ApiKeys 将只有用户范围,因此我可以在其上过滤路由。单个真实用户,无论使用何种 App,都将使用 JWT 令牌登录并发出请求。因此,每个请求都会有一个 ApiKey(基于所使用的应用程序)和一个 JWT 令牌(用于识别用户):

ApiKey 将由第 3 方服务器添加到标头中,JWT 令牌将由用户浏览器的 web_storage (检索并)添加到标头中。

我不确定您是否说 API 密钥也是 JWT 令牌,但如果不是,那么我也会使用 JWT 令牌作为 API 密钥,但具有特定于每个第三方的范围/角色客户。

加倍努力

我不抗拒在任何安全问题中包含我回答 OWASP 基金会所做的出色工作,这个案例与您最相关的是Web 安全测试指南

OWASP Web 安全测试指南包括用户可以在自己的组织中实施的“最佳实践”渗透测试框架和描述测试最常见 Web 应用程序和 Web 服务安全问题的技术的“低级”渗透测试指南。

于 2020-02-21T15:12:53.040 回答