我正在实现需要身份验证的REST服务。我无法存储任何每个用户的状态(例如随机生成的令牌),因为我的服务无法直接访问数据库,而只能直接访问另一个后端服务。

我想出的解决方案是用户验证时创建一个JSON Web令牌(JWT)。 JWT声明集在“主题”(“ sub”)字段中包含用户ID。然后,服务器使用带有256位密钥(“ enc”:“ A256GCM”)的AES GCM直接加密声明集(“ alg”:“ dir”),从而创建JWE。服务启动后立即生成密钥并将其存储在内存中。

要进行身份验证,客户端将提交用户名/密码,服务器将使用上述令牌进行响应。然后,客户端随每个后续请求发送该令牌。

当客户端向后续请求提交令牌时,服务器使用密钥对其解密,并假定“ sub”字段中的用户ID为当前用户的ID,无需进行任何进一步的身份验证检查。令牌到期由JWT声明集中的“ exp”字段处理。

客户端与服务器之间的连接将使用SSL / TLS,因此令牌不会泄漏。

由于我不信任自己编写正确的密码代码,因此我正在使用此库来创建和读取JWT。

我的问题:


以上方法安全吗?攻击者可以通过操纵令牌模拟其他用户吗?
方法是否过于复杂?使用MAC(换句话说:JWS)代替加密是否具有相同的安全性? (或者可能更多,因为它更简单并且犯错的机会更少)。 JWT声明集中没有什么特别的秘密,而且用户知道自己的ID没关系。
我选择的JWE算法和加密是否合适?

对于JWE“ alg”,我正在使用的库支持直接加密(使用密钥直接对声明集进行加密)和RSA(生成新密钥以对每个令牌的声明集进行加密,并使用RSA公钥)。我之所以选择前者,是因为它比RSA密钥更容易生成对称密钥。
对于JWE“ enc”,该库支持AES GCM和AES CBC HMAC SHA2(具有各种位长)。我任意选择了GCM。




#1 楼

您的基本方法是有效的:在用户登录时生成JWT,期望后续消息携带JWT,如果JWT有效,则在这些后续消息中信任JWT中的subject字段。但是,您需要注意几件事:


正如Daisetsu所说,您可以使用MAC(“ alg”:“ HS256”),因为MAC是专门设计用来防止更改的。有效负载,而加密算法通常(反直觉)则不是。但是,由于您是在GCM模式下专门使用AES,因此您已经获得了防篡改加密(“认证加密”),因此这并不是真正的问题。例如,我可以使用{“ sub”:“ me”,“ alg”:“ none”}来调用您的服务,虽然JWT在某种意义上是有效的,但您不想接受它。
由于JWT是草案,而不是标准,因此可能会发生变化。如果变化足够大,则您正在使用的库可能必须以破坏与代码兼容性的方式进行更改。
如果无法存储任何服务器端状态,则当JWT失效时,您将无法使JWT无效。用户注销。实际上,您的服务没有注销功能,这可能是一个安全问题,尤其是如果您将过期时间设置得太远。登录但没有有效的JWT。这可能会导致尴尬的错误处理和用户工作流问题。

由于您说您的服务器无权访问数据库,因此我认为实际登录是在其他地方处理的,也许是您提到的后端服务器。您没有说服务器如何知道用户刚刚登录。根据用户对您的服务与他们所知道的东西之间的关系的了解,上面的最后两点可能没有什么根据。

评论


GCM是一种经过身份验证的加密模式,因此它确实提供了身份验证。

– Dietrich Epp
2014年9月16日19:34

关于您的#4,在无状态服务器方案中,在客户端上删除令牌是否构成注销?

–丹
2015年6月2日19:24

您不能信任客户端。如果有人记下该令牌(或者它以某种方式泄漏),则他可以转到另一台计算机上并具有立即访问权限-无需再次登录。根据您的要求,这可能是问题,也可能不是问题。

– Neeme Praks
2015年9月18日在20:22



#2 楼

如果所有数据都不敏感,那么您要做的就是保留数据的完整性。签署(JWS)令牌应该足以做到这一点。

如果您只是进行签名,则可以使用HMAC SHA-256。记住要设置令牌的到期时间,并检查用户是否手动注销(在这种情况下,使令牌无效)。一旦考虑了到期和注销,就不必担心太多了(在算法上很明智)。 (应该)有人在单个会话中破解SHA-256的机会相对较低(假设您需要以合理的间隔重新进行身份验证)。

与签名一样,请确保您提供要签名的内容(用户名,帐户类型等),切勿让任何用户定义的数据签名,否则您可能处于危险境地。

免责声明:
此职位严格是我的看法。我没有对答案的可靠性或适用性提出任何要求。您应该始终咨询安全专家,以讨论您对安全实施的特定关注点。这纯粹是教育。