4.6.10-Testing_JSON_Web_Tokens

本文详细介绍了如何测试JSONWebTokens(JWT)的安全性,包括检查敏感信息、防止篡改,以及对JWT结构(头、载荷和签名)的分析。强调了验证签名、使用安全算法和妥善存储的重要性,以及常见漏洞案例和修复措施。
摘要由CSDN通过智能技术生成

Testing JSON Web Tokens

ID
WSTG-SESS-10

Summary

JSON Web Tokens (JWTs) are cryptographically signed JSON tokens, intended to share claims between systems. They are frequently used as authentication or session tokens, particularly on REST APIs.

JWTs are a common source of vulnerabilities, both in how they are in implemented in applications, and in the underlying libraries. As they are used for authentication, a vulnerability can easily result in a complete compromise of the application.

Test Objectives

  • Determine whether the JWTs expose sensitive information.
  • Determine whether the JWTs can be tampered with or modified.

How to Test

Overview

JWTs are are made up of three components:

  • The header
  • The payload (or body)
  • The signature

Each component is Base64 encoded, and they are separated by periods (.). Note that the Base64 encoding used in a JWT strips out the equals signs (=), so you may need to add these back in to decode the sections.

Analyse the Contents

Header

The header defines the type of token (typically JWT), and the algorithm used for the signature. An example decoded header is shown below:

{
  "alg": "HS256",
  "typ": "JWT"
}

There are three main types of algorithms that are used to calculate the signatures:

AlgorithmDescription
HSxxxHMAC using a secret key and SHA-xxx.
RSxxx and PSxxxPublic key signature using RSA.
ESxxxPublic key signature using ECDSA.

There are also a wide range of other algorithms which may be used for encrypted tokens (JWEs), although these are less common.

Payload

The payload of the JWT contains the actual data. An example payload is shown below:

{
  "username": "admininistrator",
  "is_admin": true,
  "iat": 1516239022,
  "exp": 1516242622
}

The payload is it not usually encrypted, so review it to determine whether there is any sensitive of potentially inappropriate data included within it.

This JWT includes the username and administrative status of the user, as well as two standard claims (iat and exp). These claims are defined in RFC 5719, a brief summary of them is given in the table below:

ClaimFull NameDescription
issIssuerThe identity of the party who issued the token.
iatIssued AtThe Unix timestamp of when the token was issued.
nbfNot BeforeThe Unix timestamp of earliest date that the token can be used.
expExpiresThe Unix timestamp of when the token expires.
Signature

The signature is calculated using the algorithm defined in the JWT header, and then Base64 encoded and appended to the token. Modifying any part of the JWT should cause the signature to be invalid, and the token to be rejected by the server.

Review Usage

As well as being cryptographically secure itself, the JWT also needs to be stored and sent in a secure manner. This should include checks that:

The validity of the JWT should also be reviewed, based on the iat, nbf and exp claims, to determine that:

  • The JWT has a reasonable lifespan for the application.
  • Expired tokens are rejected by the application.

Signature Verification

One of the most serious vulnerabilities encountered with JWTs is when the application fails to validate that the signature is correct. This usually occurs when a developer uses a function such as the NodeJS jwt.decode() function, which simply decodes the body of the JWT, rather than jwt.verify(), which verifies the signature before decoding the JWT.

This can be easily tested for by modifying the body of the JWT without changing anything in the header or signature, submitting it in a request to see if the application accepts it.

The None Algorithm

As well as the public key and HMAC-based algorithms, the JWT specification also defines a signature algorithm called none. As the name suggests, this means that there is no signature for the JWT, allowing it to be modified.

This can be tested by modifying the signature algorithm (alg) in the JWT header to none, as shown in the example below:

{
        "alg": "none",
        "typ": "JWT"
}

The header and payload are then re-encoded with Base64, and the signature is removed (leaving the trailing period). Using the header above, and the payload listed in the payload section, this would give the following JWT:

eyJhbGciOiAibm9uZSIsICJ0eXAiOiAiSldUIn0K.eyJ1c2VybmFtZSI6ImFkbWluaW5pc3RyYXRvciIsImlzX2FkbWluIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjI0MjYyMn0.

Some implementations try and avoid this by explicitly blocking the use of the none algorithm. If this is done in a case-insensitive way, it may be possible to bypass by specifying an algorithm such as NoNe.

ECDSA “Psychic Signatures”

A vulnerability was identified in Java version 15 to 18 where they did not correctly validate ECDSA signatures in some circumstances (CVE-2022-21449, known as “psychic signatures”). If one of these vulnerable versions is used to parse a JWT using the ES256 algorithm, this can be used to completely bypass the signature verification by tampering the body and then replacing the signature with the following value:

MAYCAQACAQA

Resulting in a JWT which looks something like this:

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6InRydWUifQ.MAYCAQACAQA

Weak HMAC Keys

If the JWT is signed using a HMAC-based algorithm (such as HS256), the security of the signature is entirely reliant on the strength of the secret key used in the HMAC.

If the application is using off-the-shelf or open source software, the first step should be go investigate the code, and see whether there is default HMAC signing key that is used.

If there isn’t a default, then it may be possible to crack guess or brute-force they key. The simplest way to do this is to use the crackjwt.py script, which simply requires the JWT and a dictionary file.

A more powerful option is to convert the JWT into a format that can be used by John the Ripper using the jwt2john.py script. John can then be used to carry out much more advanced attacks against the key.

If the JWT is large, it may exceed the maximum size supported by John. This can be worked around by increasing the value of the SALT_LIMBS variable in /src/hmacSHA256_fmt_plug.c (or the equivalent file for other HMAC formats) and recompiling John, as discussed in the following GitHub issue.

If this key can be obtained, then it is possible to create and sign arbitrary JWTs, which usually results in a complete compromise of the application.

HMAC vs Public Key Confusion

If the application uses JWTs with public key based signatures, but does not check that the algorithm is correct, this can potentially exploit this in a signature type confusion attack. In order for this to be successful, the following conditions need to be met:

  1. The application must expect the JWT to be signed with a public key based algorithm (i.e, RSxxx or ESxxx).
  2. The application must not check which algorithm the JWT is actually using for the signature.
  3. The public key used to verify the JWT must be available to the attacker.

If all of these conditions are true, then an attacker can use the public key to sign the JWT using a HMAC based algorithm (such as HS256). For example, the Node.JS jsonwebtoken library uses the same function for both public key and HMAC based tokens, as shown in the example below:

// Verify a JWT signed using RS256
jwt.verify(token, publicKey);

// Verify a JWT signed using HS256
jwt.verify(token, secretKey);

This means that if the JWT is signed using publicKey as a secret key for the HS256 algorithm, the signature will be considered valid.

In order to exploit this issue, the public key must be obtained. The most common way this can happen is if the application re-uses the same key for both signing JWTs and as part of the TLS certificate. In this case, the key can be downloaded from the server using a command such as the following:

openssl s_client -connect example.org:443 | openssl x509 -pubkey -noout

Alternatively, the key may be available from a public file on the site at a common location such as /.well-known/jwks.json.

In order to test this, modify the contents of the JWT, and then use the previously obtained public key to sign the JWT using the HS256 algorithm. This is often difficult to perform when testing without access to the source code or implementation details, because the format of the key must be identical to the one used by the server, so issues such as empty space or CRLF encoding may result in the keys not matching.

Attacker Provided Public Key

The JSON Web Signature (JWS) standard (which defines the header and signatures used by JWTs) allows the key used to sign the token to be embedded in the header. If the library used to validate the token supports this, and doesn’t check the key against a list of approved keys, this allows an attacker to sign an JWT with an arbitrary key that they provide.

There are a variety of scripts that can be used to do this, such as jwk-node-jose.py or jwt_tool.

Related Test Cases

Remediation

  • Use a secure and up to date library to handle JWTs.
  • Ensure that the signature is valid, and that it is using the expected algorithm.
  • Use a strong HMAC key or a unique private key to sign them.
  • Ensure that there is no sensitive information exposed in the payload.
  • Ensure that JWTs are securely stored and transmitted.
  • See the OWASP JSON Web Tokens Cheat Sheet.

Tools

References

### 回答1: tokenizer.convert_tokens_to_ids是一个函数,用于将token转换为对应的id。在自然语言处理中,我们通常将文本转换为token序列,然后再将token序列转换为对应的id序列,以便于模型的输入和处理。这个函数是在tokenization模块中定义的,常用于BERT等预训练模型的输入处理中。 ### 回答2: tokenizer.convert_tokens_to_ids是一个在自然语言处理中十分常用的函数,这个函数的作用是将文本序列中的每个单词或符号转换为其对应的ID号。具体来说,tokenizer.convert_tokens_to_ids函数将接受一个文本序列作为输入参数,然后返回一个列表,列表中的每个元素都是文本序列中对应单词或符号的ID号。 tokenizer.convert_tokens_to_ids函数通常用于将文本序列中的单词或符号转化为机器能够理解和处理的数字形式。在进行自然语言处理任务时,通常需要将文本序列表示成数字矩阵,并进一步输入到神经网络中进行训练和模型预测。因此,tokenizer.convert_tokens_to_ids被广泛应用于机器翻译、情感分析、文本分类、问答系统等自然语言处理任务中。 需要注意的是,tokenizer.convert_tokens_to_ids函数的使用需要先进行初始化。对于绝大部分的预训练模型,它们都提供了相应的tokenizer,我们需要先实例化tokenizer对象并将其预训练的词表加载进来,然后才能使用tokenizer.convert_tokens_to_ids函数将文本序列转化为数字矩阵。 总之,tokenizer.convert_tokens_to_ids是一个非常常用的自然语言处理函数,它能够将文本序列中的单词或符号转化为数字矩阵,并被广泛应用于自然语言处理任务中。 ### 回答3: tokenizer.convert_tokens_to_ids是transformers库中的一个方法,主要功能是将单词(token)列表转换为对应的ID编号列表。在NLP中,文本经常需要被转换为数值型数据才能输入到神经网络中进行训练或预测。因此,经常需要将文本进行tokenization(分词)并将单词映射到对应的ID编号。这个过程就是使用tokenizer.convert_tokens_to_ids方法可以实现的。 该方法所需参数为一个单词列表,该列表包含分词后的文本。它可以用来将句子(例如英文句子)转换为一个数字列表,将每个单词替换为对应的数字编号。这些数字可以表示单词在字典中的位置。例如,一个长度为N的句子可以转换为一个包含N个数字的列表。 具体来说,tokenizer.convert_tokens_to_ids方法将分词后的单词列表作为输入,返回一个包含与这些单词对应的ID编号的列表。注意,这里的ID是映射到所选tokenizer的词汇表的编号。 为了进一步了解这个方法的用法,我们可以通过以下示例进行说明: ```python from transformers import BertTokenizer tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') # 句子分词后的结果 text = "This is an example of text tokenization." tokens = tokenizer.tokenize(text) print(tokens) >>> ['this', 'is', 'an', 'example', 'of', 'text', 'token', '##ization', '.'] # 将分词后的结果转换为对应的ID ids = tokenizer.convert_tokens_to_ids(tokens) print(ids) >>> [2023, 2003, 2019, 2742, 1997, 3793, 19204, 20570, 1012] ``` 在上面的示例中,我们首先使用`BertTokenizer`进行分词处理,然后使用`convert_tokens_to_ids`将分词后的单词列表转换为对应的ID编号列表。该方法将`tokens`列表中的每个单词映射为与其对应的唯一ID编号。最终,我们可以得到一个包含所有单词ID编号的列表`ids`。通过这个方法,我们可以将文本转换为一个数字列表,使它可以输入到神经网络进行训练或其他操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值