这安全吗

在构建基于密码的身份验证流程时,我们竭尽全力遵循所有最常见的最佳实践。

是时候问问自己: 它安全吗?

传输层安全 (TLS)

我们使用“基本”身份验证方案在客户端和服务器之间传递凭据: 用户名和密码已编码,但未加密。

我们必须使用传输层安全性 (TLS) 来确保没有人能够窃听客户端和服务器之间的通信,从而窃取用户凭据(即中间人攻击 - MITM)。

我们的 API 已通过 HTTPS 提供,因此此处无需执行任何操作。

密码重置

如果攻击者成功窃取一组有效的用户凭证,会发生什么?

密码不会过期——它们是长期有效的秘密。

目前,用户无法重置密码。这无疑是我们需要填补的一个空白。

互动类型

到目前为止,我们对 API 的调用者还比较模糊。

我们需要支持的交互类型是身份验证的关键决策因素。

我们将讨论三类调用者:

  • 其他 API(机器对机器)
  • 通过浏览器调用的人员
  • 代表人员的其他 API。

机器对机器

您的 API 的使用者可能是一台机器(例如,另一个 API)。

这在微服务架构中很常见——您的功能源于各种通过网络交互的服务。

为了显著提升我们的安全性,我们必须加入一些他们拥有的功能(例如,请求签名)或他们本身的功能(例如,IP 范围限制)。

当所有服务都归同一组织所有时,一个流行的选择是相互 TLS (mTLS)。

签名和 mTLS 都依赖于公钥加密——密钥必须进行配置、轮换和管理。只有当您的系统达到一定规模时,这种开销才是合理的。

通过 OAuth2 获取客户端凭证

另一种选择是使用 OAuth2 客户端凭证

API 不再需要管理密码(OAuth2 术语中称为客户端密钥),而将这一工作委托给集中式授权服务器。市面上有多种现成的授权服务器实现方案,包括开源软件和商业软件。您可以依赖这些方案,而不必自行开发。

调用者通过授权服务器进行身份验证,如果成功,授权服务器会授予他们一组临时凭证(JWT 访问令牌),可用于调用我们的 API。

我们的 API 可以使用公钥加密技术验证访问令牌的有效性,而无需保留任何状态。我们的 API 永远不会看到实际的密码,即客户端密钥。

JWT 验证并非没有风险,该规范充斥着危险的边缘情况

通过浏览器的人

如果我们与使用 Web 浏览器的用户打交道会怎样?

“基本”身份验证要求客户端在每次请求时都提供其凭据。

我们现在只有一个受保护的端点,但您可以轻松想象五到十个页面提供特权功能的情形。目前,“基本”身份验证会强制用户在每一个页面上提交其凭据。这不太好。

我们需要一种方法来记住用户刚刚进行过身份验证——即将某种状态附加到来自同一浏览器的一系列请求中。这可以通过会话来实现。

用户需要通过登录表单进行一次身份验证: 如果成功,服务器将生成一次性的密钥——一个经过身份验证的会话令牌。该令牌作为安全 Cookie 存储在浏览器中。

与密码不同,会话具有过期功能——这降低了有效会话令牌被泄露的可能性(尤其是在不活动的用户自动注销的情况下)。它还可以避免用户在怀疑会话被劫持时重置密码——强制注销比自动重置密码更容易被接受。

这种方法通常被称为基于会话的身份验证

联合身份

使用基于会话的身份验证,我们仍然需要处理一个身份验证步骤——登录表单。

我们可以继续开发自己的身份验证方案——即使我们放弃“基本”身份验证方案,我们学到的关于密码的所有知识仍然适用。

许多网站选择为用户提供额外的选项:通过社交账户登录——例如“使用 Google 登录”。这消除了注册流程中的障碍(无需再创建另一个密码!),

从而提高了转化率——这是一个理想的结果。

社交登录依赖于身份联合——我们将身份验证步骤委托给第三方身份提供商,然后他们会与我们共享我们请求的信息(例如,电子邮件地址、全名和出生日期)。

身份联合的常见实现依赖于 OpenID Connect,它是 OAuth2 标准之上的一个身份层。

机器对机器,代表一个人

还有一种场景:一个人授权一台机器(例如第三方服务)代表他们执行针对我们 API 的操作。

例如,一个为 Twitter 提供替代 UI 的移动应用。

需要强调的是,这与我们讨论的第一个场景(纯粹的机器对机器身份验证)有何不同。

在这种情况下,第三方服务本身无权针对我们的 API 执行任何操作。

第三方服务只有在用户授予访问权限(访问权限范围在其权限范围内)的情况下才能针对我们的 API 执行操作。

我可以安装一个移动应用来代表我发推文,但我不能授权它代表 David Guetta 发推文。

在这种情况下,“基本”身份验证非常不合适:我们不想与第三方应用共享密码。

看到我们密码的人越多,密码被泄露的可能性就越大。

此外,保留共享凭证的审计线索简直是一场噩梦。当出现问题时, 我们无法确定是谁做了什么: 是我做的吗? 是我共享凭证的二十个应用之一吗?

谁来承担责任?

这是 OAuth2 的典型场景——第三方永远无法看到我们的用户名和密码。他们从身份验证服务器收到一个不透明的访问令牌,我们的 API 知道如何检查该令牌,以授予(或拒绝)访问权限。