Blog Series #2 — Is your JWT protected?
A JWT relies on its signature to guarantee that nobody changed its contents. If the secret used for that signature is weak or exposed, an attacker can create valid tokens and manipulate the application.


JWT, or JSON Web Token, is one of the most widely used formats for authentication in web applications and APIs. After the user logs in, the server generates a token and hands it to the client, which then sends it with every request, usually in the Authorization header. From that token, the server identifies who is making the request and which permissions that person has.
The security of this model depends on a single point: the signature. It is what guarantees that the token's contents were generated by the server and were not changed along the way. When the secret used to sign the token is weak or ends up exposed, an attacker can generate their own tokens and the application starts accepting them as if they were legitimate.
What a JWT is
A JWT is made of three parts separated by dots: the header, the payload, and the signature.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJuYW1lIjoiTWFyaWEiLCJyb2xlIjoidXNlciJ9.<signature>
The header states which algorithm was used to sign the token:
{
"alg": "HS256",
"typ": "JWT"
}
The payload contains the information the application uses to identify the user, such as the identifier, the name, and the access role:
{
"sub": "123",
"name": "Maria",
"role": "user"
}
One important point is that these first two parts are not encrypted. They are only Base64-encoded, which means anyone with access to the token can read its contents. JWT was not designed to hide information, but to guarantee that it is not changed.
How the signature works
The third part of the token is the signature. With the HS256 algorithm, one of the most common, the server takes the header and the payload and computes an HMAC using a secret that only it should know.
When the token comes back in a request, the server repeats that calculation. If the result matches the signature in the token, the server considers the contents trustworthy. If someone changes any information in the payload, for example replacing "role": "user" with "role": "admin", the signature no longer matches and the token is rejected.
This means the protection of a JWT is not in the token itself, but in the secret used to sign it. As long as that secret remains unknown, nobody can produce a valid signature for a modified payload.
The risk of generating signatures on websites
During development, it is common to use websites that decode, validate, and generate JWTs. You paste the token, provide the secret, and the site shows the contents and computes the signature.
The problem appears when this is done with real tokens and secrets. By pasting the production secret into a third-party website, it is no longer only on the application's server. There is no way to guarantee what happens to that information after it is sent: it may be logged, stored, or exposed in a way entirely outside the control of whoever used it.
The same applies to the token. A production token represents a real user's session and, while it is valid, can be used by anyone who has access to it.
Generating or validating signatures should be the responsibility of the application itself, with the secret stored securely on the server. For testing, the ideal is to use tokens and secrets created specifically for that purpose.
When the secret can be cracked
Even if the secret is never pasted into any website, there is still another risk. A token signed with HS256 contains everything needed to test whether a given secret is correct: the header, the payload, and the resulting signature.
This allows an attacker who has obtained a token to try to discover the secret offline, without making any request to the application. Tools such as Hashcat, widely used for password cracking, can test an enormous number of possibilities in a short time, using lists of common words and combinations.
If the secret is short, predictable, or based on well-known values, such as the application's name, secret, 123456, or a sample value copied from the documentation, it can be discovered quickly.
The impact
With the secret in hand, the attacker has the same capability as the server: they can generate valid tokens with any content.
In practice, this allows changing the payload to impersonate another user, by changing the value of sub, or to escalate their own permissions, by changing the role field to admin. Since the signature is computed with the correct secret, the application has no way to tell this token apart from a legitimate one.
From that point on, everything the application controls based on the JWT is compromised: access to other users' accounts, admin panels, internal API endpoints, and any data that depends on that authentication.
How to protect yourself
The main measure is to use a long, randomly generated secret that cannot be guessed or found in word lists. That secret should be stored only on the server, outside the source code and the repository.
It is also important never to use production secrets or tokens on third-party websites to decode or generate signatures.
In addition, the server should explicitly define which algorithm it accepts, instead of trusting the value provided in the token's header, and tokens should have a short expiration time. In scenarios where several services need to validate tokens, asymmetric algorithms such as RS256 allow only the issuer to hold the private key used for signing, while the other services use only the public key to validate.
Bring DevSecOps into your workflow with runtz now
Start scanning for security risks. Get started at runtz.dev.



