保护 REST 身份验证的密码

信息安全 密码 哈希 休息 阿贾克斯
2021-09-12 00:55:37

我正在使用 Spring Framework 开发一个 REST 应用程序,作为要求的一部分,我们必须将系统的不同功能保护到不同的用户角色(非常标准的东西)。我当前确定当前登录用户角色的方法是,每次他们从前端调用 REST url 时,我都会在请求标头中添加一个 Base 64 编码的字符串。此字符串在解码后解析为他们的用户名和 bCrypt 生成的密码哈希,格式为 username:hashedpassword。

我有点担心这是不安全的,即使请求将通过安全的 HTTP 连接发出,因为它可能使潜在的黑客至少可以访问用户的用户名。他们无法获得密码,因为那只是一个散列值,但他们可以使用该散列值成功调用 REST API。

如何正确保护该系统?我是否需要为会话添加会话令牌或某种随机生成的密钥?

我的后续问题是我怎样才能 RESTfully 做到这一点?我在想我可以在登录时生成(使用 bCrypt)一个代表 username:hashedpassword 的哈希值,将其保存到数据库中,并在进行 REST 调用时对其进行检查。当用户注销时,只需将其设置为 null。冲洗并重复。这样,任何潜在的攻击者都只会得到一个不会暴露用户名的 bCrypt 字符串,但他们仍然可以使用该字符串来调用 REST API。

2个回答

以下链接可能会为您提供深入的答案:

请记住,最好不要在您发出的每个请求中使用用户名-密码组合。更好的做法是对用户进行身份验证,在服务器端生成令牌,将其传递给客户端(例如在 cookie 中)并将该令牌用作后续请求的身份验证。此链接可以指导您完成该过程:https ://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Session_Management_Cheat_Sheet.md 。

如果您只需要提供身份验证,但通过线路传输的数据不敏感,那么有一种更好的方法,无需 SSL 的开销/延迟。

假设您有一个移动客户端应用程序;首先通过在单独的 Web 表单(不是应用程序或 REST 服务的一部分)中提供他们的电子邮件(用户名)和密码来让用户注册或注册。然后在成功注册后,您会使用用户密钥(这可以是存储在服务器(数据库)上的用户帐户中用于对称加密的共享密钥或用于非对称加密的公钥,其中相应的私钥存储在用户帐户中)进行响应在服务器(数据库)上。这一切都是通过 SSL 使用 Web 表单完成的。

现在,当用户打开客户端应用程序时,您必须向他们询问他们的凭据,这些凭据将与对 RESTful 服务的每个请求一起发送。他们必须提供他们之前收到的姓名、密码和加密密钥。这只需要做一次。然后,该应用程序会为每个请求提供一些 http 标头,如下所示:

AUTHENTICATE> username:timestamp:encrypted{password:timestamp} /AUTHENTICATE>

请注意,{} 中的密码和时间戳都是使用用户密钥加密的。每个请求都会更新时间戳。

在执行以下操作的服务器上实施身份验证过滤器:

首先检查时间戳,如果过期(比如早于 1 秒)发送未经授权的 HTTP 响应代码。如果时间戳有效,请在您的用户帐户数据库中查找用户名。如果未找到,则发送 UNAUTHORIZED HTTP 响应。如果找到用户名,则为该用户获取存储的加密密钥(请记住,这可以是共享密钥或用户公钥的私钥)。解密加密的 {password:timestamp}。解密后的密码必须与存储在数据库中的用户密码匹配(密码本身也可以使用另一个密钥在数据库中加密以增加安全性),并且解密的时间戳还必须与上面 AUTHENTICATE 标头中发送的非加密时间戳匹配。如果不是,则发送未经授权的 HTTP 响应代码。

您还可以缓存用户详细信息以避免对每个请求进行数据库查找。

现在,如果有人在窥探并拦截请求,他们将无法重新使用它来获得访问权限,因为时间戳将无效,或者如果他们将未加密的时间戳更新为有效,它将与加密的时间戳不匹配(之后身份验证过滤器对其进行解密)。

与使用单个应用程序密钥相比,这种方法的另一个优点是,您现在可以通过在数据库中的用户帐户上设置到期日期来完全控制谁可以访问您的服务(有效地实现基于订阅的服务)。这很好,因为起初您可能希望通过试用订阅(例如 1 年免费)获得尽可能多的用户,然后如果他们没有支付延长帐户到期日的费用,则稍后阻止对该用户的访问 :)