![JSON Web令牌(JWT)及访问控制绕过 JSON Web令牌(JWT)及访问控制绕过]()
JSON Web令牌是一种广泛用于商业应用程序的访问令牌。它们基于JSON格式,并包含一个令牌签名以确保令牌的完整性。
本文主要讨论使用JSON Web令牌(以及一般基于签名的令牌)的安全隐患,以及攻击者如何利用它们绕过访问控制。
JSON Web令牌由三个组件组成:标头(header)、 有效载荷(payload)和签名(signature)。
JSON Web令牌的标头部分标识用于生成签名的算法。它是JSON blob的base64url编码字符串,如下所示:
{
"alg" : "HS256",
"typ" : "JWT"
}base64url encoded string: eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K
base64url编码是针对 URL格式的base64的修改版本。它类似于base64,但使用不同的非字母数字字符并省略填充。
有效载荷部分包含实际用于访问控制的信息。在令牌中使用之前,此部分也是base64url编码的。
{
"user_name" : "admin",
}base64url encoded string: eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg
签名是用于验证令牌未被篡改的部分。它的计算方法是将标头与有效载荷串联在一起,然后使用标头中指定的算法进行签名。
signature = HMAC-SHA256(base64urlEncode(header) + '.' + base64urlEncode(payload), secret_key)
对于这个特定的令牌,字符串“eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg”使用HS256算法和密钥“key”进行签名。生成的字符串是 4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M。
通过将每个部分(标头、有效载荷和签名)与每个部分之间“.”连接起来,可获得完整的令牌,如下所示:
eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M
如果正确实施,JSON Web令牌提供了一种安全的方式来识别用户,因为有效载荷部分中包含的数据无法被篡改。由于用户无权访问密钥,因此他不能自己签署令牌。 但如果实施不当,攻击者可以通过多种方式绕过安全机制并伪造任意令牌。
攻击者可以伪造自己的令牌的方法之一是篡改标头的alg字段。如果应用程序不限制 JWT中使用的算法类型,攻击者可以指定使用哪种算法,这可能会损害令牌的安全性。
JWT支持“None”算法。如果alg字段设置为“none”,则任何令牌在其签名部分设置为空时都将被视为有效。例如,以下令牌将被视为有效:
eyAiYWxnIiA6ICJOb25lIiwgInR5cCIgOiAiSldUIiB9Cg.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.
它只是这两个blob的base64url编码版本,不存在签名。
{
"alg" : "none",
"typ" : "JWT"
}{
"user" : "admin"
}
此功能最初用于调试目的。但是,如果在生产环境中将其关闭,它将允许攻击者通过将alg字段设置为“none”来伪造他们想要的任何令牌。然后,他们可以使用伪造的令牌冒充网站上的任何人。
用于JWT的两种最常见的算法类型是HMAC和RSA。使用HMAC,令牌将使用密钥签名,然后使用相同的密钥进行验证。对于RSA,令牌将首先使用私钥创建,然后使用相应的公钥进行验证。
HMAC -> signed with a key, verified with the same keyRSA -> signed with a private key, verified with the corresponding public key
HMAC令牌的密钥和RSA令牌的私钥都必须保密,因为它们用于对令牌进行签名。
现在假设有一个应用程序最初设计为使用RSA令牌。令牌使用私钥A签名,该私钥对公众保密。然后使用公钥B验证令牌,任何人都可以使用该公钥。只要令牌始终被视为RSA令牌,这就没有问题。
Token signed with key A -> Token verified with key B (RSA scenario)
现在,如果攻击者将alg更改为 HMAC,他可能能够通过使用RSA公钥B伪造的令牌进行签名来创建有效的令牌。
这是因为本来在用RSA对令牌进行签名的时候,程序是用RSA公钥B验证的,当签名算法切换到HMAC时,还是用RSA公钥B来验证令牌,但是这次令牌可以使用相同的公钥B签名(因为它使用的是HMAC)。
Token signed with key B -> Token verified with key B (HMAC scenario)
令牌的签名也可能在到达应用程序后从未经过验证。这样,攻击者就可以通过提供无效签名简单地绕过安全机制。
攻击者可以从很多信息入手,如用于对令牌进行签名的算法、已签名的有效载荷以及由此产生的签名。如果用于签署令牌的密钥不够复杂,则攻击者也许可以轻松地对其进行暴力破解。
如果攻击者无法暴力破解密钥,则他可能会尝试泄露密钥。如果存在另一个漏洞(如目录遍历、XXE、SSRF),允许攻击者读取存储密钥值的文件,则攻击者可以窃取密钥并签署任意令牌。
KID代表“密钥 ID”。它是JWT中的一个可选标头字段,它使开发人员可以指定用于验证令牌的密钥。KID参数的正确用法如下所示:
{
"alg" : "HS256",
"typ" : "JWT",
"kid" : "1" // use key number 1 to verify the token
}
由于这个字段是由用户控制的,它可以被攻击者操纵并导致危险的后果。
由于KID通常用于从文件系统中检索密钥文件,如果在使用前没有对其进行清理,则可能会导致目录遍历攻击。在这种情况下,攻击者将能够指定文件系统中的任何文件作为用于验证令牌的密钥。
“kid”: “../../public/css/main.css”
例如,攻击者可以强制应用程序使用公开可用的文件作为密钥,并使用该文件签署 HMAC令牌。
KID还可用于从数据库中检索密钥。在这种情况下,可能会利用SQL注入来绕过JWT签名。
如果KID参数上可能存在SQL注入,则攻击者可以使用此注入返回其想要的任何值。
“kid”: "aaaaaaa' UNION SELECT 'key';
例如,上面的注入将导致应用程序返回字符串“key”(因为名为“aaaaaaa”的键在数据库中不存在)。然后将使用字符串“key”作为密钥来验证令牌。
除了密钥ID,JSON Web令牌标准还使开发人员能够通过URL指定密钥。
JKU代表“JWK设置URL”。它是一个可选的标头字段,用于指定指向一组用于验证令牌密钥的URL。如果该字段被允许且未适当限制,则攻击者可以托管自己的密钥文件,并指定应用程序使用它来验证令牌。
jku URL -> file containing JWK set -> JWK used to verify the token
可选的JWK (JSON Web Key) 标头参数允许攻击者将用于验证令牌的密钥直接嵌入令牌中。
与jku和jwk标头类似,x5u和x5c标头参数允许攻击者指定用于验证令牌的公钥证书或证书链。x5u以URI的形式指定信息,而x5c允许将证书值嵌入到令牌中。
评论