401 和/或 403 以及一个关于安全的 RESTful 故事
401 和/或 403 以及一个关于安全的 RESTful 故事
在设计 RESTful 服务时,不可避免地会遇到使用哪个 HTTP 状态码的问题。 然而,关于 401 和 403 状态码存在一些微妙之处。 你可以在这里和那里读到建议使用
- 401 用于未提供访问令牌或访问令牌无效的情况
- 403 用于访问令牌有效,但需要更多权限的情况
人们会遵循这些建议,而没有考虑到安全问题。
问题在于,这种实现可能会泄露敏感信息。 这是我对上述文章的回复。 在密码学中,这被称为 预言机,可能导致 非常严重的攻击。
如果攻击者在多次失败的尝试(返回 401)后看到 403 状态码,这意味着攻击者尝试的任何内容都通过了身份验证阶段,但未能通过授权。 恭喜,他们刚刚找到了有效的凭据集(或令牌等),而你的系统以“明文”形式告知了他们。 这类似于 “用户名或密码无效”的最佳实践(不要阅读像 这篇文章这样的内容!)。
如何避免这个问题? 坚持使用一个状态码(例如,403)来表示两种情况(身份验证失败和授权失败),避免过于友好(或对客户端友好)。 你可以记录事件并向客户端返回一个唯一的请求 ID。 如果真正的客户端报告了问题,该请求 ID 应该有助于在日志中找到更多详细信息。 当然,还要跟踪/监控所有身份验证/授权失败,以进行异常检测。
现在是有趣的部分,将上述所有内容作为数学证明。 假设一个系统中,可能的凭据的最大数量为 N,注册用户数为 M,其中 K 个用户有权访问攻击者试图暴力破解的特定资源,N>M≥ K。 让我们考虑以下命题/事件
- A - 通过访问给定资源来猜测凭据。
- B - 系统设计为返回:HTTP 200 用于具有访问权限的有效凭据,HTTP 403 用于没有访问权限的有效凭据,HTTP 401 用于无效凭据。 为了简单起见,我们可以说 B={200}∪ {403}∪ {401}。
- C - 系统设计为返回:HTTP 200 用于具有访问权限的有效凭据,HTTP 403 用于没有访问权限的有效凭据或无效凭据。 或者 C={200}∪ {403}
换句话说
- 在 B 给定 A 的基数是:K 个 HTTP 200 的可能情况,加上 M-K 个 HTTP 403 的可能情况。 总计为 M。
- 在 C 给定 A 的基数是:K 个 HTTP 200 的可能情况,这也总计为 K。
现在让我们计算概率
P(A | B)=M⁄N 和 P(A | C)=K⁄N
显然(因为 M ≥ K)
P(A | B) ≥ P(A | C)
这意味着,像 B 这样设计的系统会给攻击者更大的机会来猜测凭据。 显然,如果所有注册用户都具有访问资源的权限(即 K=M),则无关紧要。 讨论结束!
后记: 我计算概率的方式可能看起来有点粗略。 如果需要更数学化的版本,请访问原始帖子。