跳转到主要内容
Chinese, Simplified

哪些库容易受到攻击以及如何防止它们。

 

TL; DR:如果您使用带有非对称密钥的node-jsonwebtokenpyjwtnamshi/josephp-jwt or jsjwtRS256,RS384,RS512,ES256,ES384,ES512),请更新到最新版本。有关易受攻击库的更多信息,请参阅jwt.io。 (2015-04-20更新)

这是来自Tim McLean的客座文章,他是Auth0安全研究员名人堂成员。蒂姆通常在www.timmclean.net上发表博客

最近,在审查各种JSON Web令牌实现的安全性时,我发现许多具有关键漏洞的库允许攻击者绕过验证步骤。在许多实现和语言中都发现了同样的两个缺陷,所以我认为准确地写出问题的位置会很有帮助。我认为对标准的更改有助于防止未来的漏洞。

“我发现许多具有严重漏洞的库允许攻击者绕过验证步骤。”


对于那些不熟悉的人来说, JSON Web Token (JWT) 是一种创建令牌的标准,可以断言一些声明。例如,服务器可以生成具有声明“以管理员身份登录”并将其提供给客户端的令牌。然后,客户端可以使用该令牌来证明他们以管理员身份登录。令牌由服务器密钥签名,因此服务器能够验证令牌是否合法。

JWT通常有三个部分:标题,有效负载和签名。

标头标识用于生成签名的算法,看起来像这样:

header = '{"alg":"HS256","typ":"JWT"}'


HS256表示此令牌使用HMAC-SHA256签名。

有效负载包含我们希望提出的声明:

payload = '{"loggedInAs":"admin","iat":1422779638}'


正如JWT规范中所建议的那样,我们包含一个名为iat的时间戳,即“发布时”的缩写。

签名由base64url编码头和有效负载计算,并以句点作为分隔符连接:

key = 'secretkey'

unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload) signature = HMAC-SHA256(key, unsignedToken)


总而言之,我们base64url编码签名,并使用句点将这三个部分连接在一起:

token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)

# token is now: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYW

RtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRg

CzcmJmMjLiuyu5CSpyHI


很棒。那么,那有什么不对?


好吧,让我们尝试验证令牌。

首先,我们需要确定用于生成签名的算法。没问题,标题中有一个alg字段告诉我们。

但是等等,我们还没有验证这个令牌,这意味着我们还没有验证头。这使我们处于一个尴尬的境地:为了验证令牌,我们必须允许攻击者选择我们用来验证签名的方法。

这对某些实现具有灾难性的影响。

迎接“None”算法


无算法是JWT的一个奇怪的补充。它旨在用于已经验证令牌完整性的情况。有趣的是,它是必须实施的两种算法之一(另一种是HS256)。

不幸的是,一些库处理了使用无算法签名的令牌作为带有经过验证的签名的有效令牌。结果?任何人都可以使用他们想要的任何有效负载创建自己的“签名”令牌,允许在某些系统上进行任意帐户访问。

将这样的标记放在一起很容易。修改上面的示例标题以包含 "alg": "none" 而不是HS256。对有效负载进行任何所需的更改。使用空签名(signature = "")。

大多数(希望所有?)实现现在都有一个基本检查来防止这种攻击:如果提供了密钥,那么使用none算法的令牌验证将失败。这是一个好主意,但它并没有解决潜在的问题:攻击者控制算法的选择。让我们继续挖掘。

RSA还是HMAC?


JWT规范还定义了许多非对称签名算法(基于RSA和ECDSA)。使用这些算法,使用私钥创建和签名令牌,但使用相应的公钥进行验证。这非常简洁:如果您发布公钥但保留私钥,只有您可以签署令牌,但任何人都可以检查给定令牌是否正确签名。

我看过的大多数JWT库都有这样的API:

# sometimes called "decode"

verify(string token, string verificationKey)

# returns payload if valid token, else throws an error


在使用HMAC签名的系统中,verificationKey将是服务器的秘密签名密钥(因为HMAC使用相同的密钥进行签名和验证):

verify(clientToken, serverHMACSecretKey)


在使用非对称算法的系统中,verificationKey将是应该验证令牌的公钥:

verify(clientToken, serverRSAPublicKey)


不幸的是,攻击者可以滥用此功能。如果服务器期望使用RSA签名的令牌,但实际上接收到使用HMAC签名的令牌,则会认为公钥实际上是HMAC密钥。

这怎么是灾难? HMAC密钥应该保密,而公钥是公共密钥。这意味着您的典型 ski mask-wearing attacker 可以访问公钥,并可以使用此伪造服务器将接受的令牌。

这样做非常简单。首先,抓住您最喜欢的JWT库,并为您的令牌选择一个有效负载。然后,获取服务器上使用的公钥作为验证密钥(最有可能采用基于文本的PEM格式)。最后,使用PEM格式的公钥作为HMAC密钥签署您的令牌。实质上:

forgedToken = sign(tokenPayload, 'HS256', serverRSAPublicKey)


最棘手的部分是确保serverRSAPublicKey与服务器上使用的验证密钥相同。字符串必须完全匹配才能使攻击工作 - 完全相同的格式,并且没有额外或缺少的换行符。

最终结果?知道公钥的任何人都可以伪造将通过验证的令牌。

对库开发人员的建议


我建议JWT库在其验证函数中添加一个算法参数:

verify(string token, string algorithm, string verificationKey)


服务器应该已经知道它用于签署令牌的算法,并且允许攻击者提供此值是不安全的。

有些人可能会争辩说,出于兼容性原因,某些服务器需要支持多个算法。在这种情况下,可以(并且应该)为每个支持的算法使用单独的密钥。 JWT可以方便地为此提供“密钥ID”字段(孩子)。由于服务器可以使用密钥ID来查找密钥及其相应的算法,因此攻击者无法再控制密钥用于验证的方式。在任何情况下,我都不认为JWT库甚至不应该查看标题中的alg字段,除非可以检查它是否与预期的算法匹配。

任何使用JWT实现的人都应该确保拒绝具有不同签名类型的令牌。一些库有一个可选的白名单或黑名单算法机制;利用它,否则你可能会面临风险。更好的是:制定一项政策,对用于提供任务关键功能的任何开源库执行安全审核。

改进JWT / JWS标准


我想建议弃用标题的alg字段。正如我们在这里看到的,它的滥用会对JWT / JWS实施的安全性产生破坏性影响。据我所知,密钥ID提供了一个充分的选择。这保证了对规范的改变:JWT库由于依赖于alg而继续编写有安全漏洞。

JWT(和JOSE)提供了一个拥有跨平台安全加密套件的机会

旁白:将JWT实施委托给专家


JWT是OpenID Connect标准不可或缺的一部分,OpenID Connect标准是位于OAuth2框架之上的标识层。 Auth0是OpenID Connect认证的身份平台。这意味着如果您选择Auth0,您可以确定它与遵循规范的任何第三方系统100%可互操作。

OpenID Connect规范要求使用JWT格式的ID令牌,其中包含以声明形式表示的用户配置文件信息(例如用户名和电子邮件)。这些声明是关于用户的声明,如果令牌的使用者可以验证其签名,则可以信任该声明。

虽然OAuth2规范没有规定访问令牌的格式,用于授权应用程序代表用户访问API,但业界也广泛接受JWT的使用。

作为开发人员,您不必担心直接验证,验证或解码服务中与身份验证相关的JWT。您可以使用Auth0的现代SDK来处理JWT的正确实施和使用,因为他们知道JWT遵循最新的行业最佳实践并定期更新以解决已知的安全风险。

例如,用于 Auth0 SDK for Single Page Applications提供了一种从ID令牌auth0.getUser中提取用户信息的方法。

如果您想试用Auth0平台,请注册免费帐户并开始使用!使用您的免费帐户,您将可以使用以下功能:


要了解有关JWT的更多信息,它们的内部结构,可以与它们一起使用的不同类型的算法以及它们的其他常见用途,请查看JWT Handbook.。

原文:https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/

本文:  http://pub.intelligentx.net/node/507

讨论:请加入知识星球或者小红圈【首席架构师圈】

Article
知识星球
 
微信公众号
 
视频号