
org.sonar.l10n.java.rules.java.S5659.html Maven / Gradle / Ivy
This vulnerability allows forging of JSON Web Tokens to impersonate other users.
Why is this an issue?
JSON Web Tokens (JWTs), a popular method of securely transmitting information between parties as a JSON object, can become a significant security
risk when they are not properly signed with a robust cipher algorithm, left unsigned altogether, or if the signature is not verified. This
vulnerability class allows malicious actors to craft fraudulent tokens, effectively impersonating user identities. In essence, the integrity of a JWT
hinges on the strength and presence of its signature.
What is the potential impact?
When a JSON Web Token is not appropriately signed with a strong cipher algorithm or if the signature is not verified, it becomes a significant
threat to data security and the privacy of user identities.
Impersonation of users
JWTs are commonly used to represent user authorization claims. They contain information about the user’s identity, user roles, and access rights.
When these tokens are not securely signed, it allows an attacker to forge them. In essence, a weak or missing signature gives an attacker the power to
craft a token that could impersonate any user. For instance, they could create a token for an administrator account, gaining access to high-level
permissions and sensitive data.
Unauthorized data access
When a JWT is not securely signed, it can be tampered with by an attacker, and the integrity of the data it carries cannot be trusted. An attacker
can manipulate the content of the token and grant themselves permissions they should not have, leading to unauthorized data access.
How to fix it in Java JWT
Code examples
The following code contains examples of JWT encoding and decoding without a strong cipher algorithm.
Noncompliant code example
import io.jsonwebtoken.Jwts;
public void encode() {
Jwts.builder()
.setSubject(USER_LOGIN)
.compact(); // Noncompliant
}
import io.jsonwebtoken.Jwts;
public void decode() {
Jwts.parser()
.setSigningKey(SECRET_KEY)
.parse(token)
.getBody(); // Noncompliant
}
Compliant solution
import io.jsonwebtoken.Jwts;
public void encode() {
Jwts.builder()
.setSubject(USER_LOGIN)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
When using Jwts.parser()
, make sure to call parseClaimsJws
instead of parse
as it throws exceptions for
invalid or missing signatures.
import io.jsonwebtoken.Jwts;
public void decode() {
Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
How does this work?
Always sign your tokens
The foremost measure to enhance JWT security is to ensure that every JWT you issue is signed. Unsigned tokens are like open books that anyone can
tamper with. Signing your JWTs ensures that any alterations to the tokens after they have been issued can be detected. Most JWT libraries support a
signing function, and using it is usually as simple as providing a secret key when the token is created.
Choose a strong cipher algorithm
It is not enough to merely sign your tokens. You need to sign them with a strong cipher algorithm. Algorithms like HS256 (HMAC using SHA-256) are
considered secure for most purposes. But for an additional layer of security, you could use an algorithm like RS256 (RSA Signature with SHA-256),
which uses a private key for signing and a public key for verification. This way, even if someone gains access to the public key, they will not be
able to forge tokens.
Verify the signature of your tokens
Resolving a vulnerability concerning the validation of JWT token signatures is mainly about incorporating a critical step into your process:
validating the signature every time a token is decoded. Just having a signed token using a secure algorithm is not enough. If you are not validating
signatures, they are not serving their purpose.
Every time your application receives a JWT, it needs to decode the token to extract the information contained within. It is during this decoding
process that the signature of the JWT should also be checked.
To resolve the issue, follow these instructions:
- Use framework-specific functions for signature verification: Most programming frameworks that support JWTs provide specific functions to not
only decode a token but also validate its signature simultaneously. Make sure to use these functions when handling incoming tokens.
- Handle invalid signatures appropriately: If a JWT’s signature does not validate correctly, it means the token is not trustworthy, indicating
potential tampering. The action to take when encountering an invalid token should be denying the request carrying it and logging the event for
further investigation.
- Incorporate signature validation in your tests: When you are writing tests for your application, include tests that check the signature
validation functionality. This can help you catch any instances where signature verification might be unintentionally skipped or bypassed.
By following these practices, you can ensure the security of your application’s JWT handling process, making it resistant to attacks that rely on
tampering with tokens. Validation of the signature needs to be an integral and non-negotiable part of your token handling process.
Going the extra mile
Securely store your secret keys
Ensure that your secret keys are stored securely. They should not be hard-coded into your application code or checked into your version control
system. Instead, consider using environment variables, secure key management systems, or vault services.
Rotate your secret keys
Even with the strongest cipher algorithms, there is a risk that your secret keys may be compromised. Therefore, it is a good practice to
periodically rotate your secret keys. By doing so, you limit the amount of time that an attacker can misuse a stolen key. When you rotate keys, be
sure to allow a grace period where tokens signed with the old key are still accepted to prevent service disruptions.
How to fix it in Auth0 JWT
Code examples
The following code contains examples of JWT encoding and decoding without a strong cipher algorithm.
Noncompliant code example
import com.auth0.jwt.JWT;
public void encode() {
JWT.create()
.withSubject(SUBJECT)
.sign(Algorithm.none()); // Noncompliant
}
import com.auth0.jwt.JWT;
public void decode() {
JWTVerifier verifier = JWT.require(Algorithm.none()) // Noncompliant
.withSubject(LOGIN)
.build();
}
Compliant solution
import com.auth0.jwt.JWT;
public void encode() {
JWT.create()
.withSubject(SUBJECT)
.sign(Algorithm.HMAC256(SECRET_KEY));
}
import com.auth0.jwt.JWT;
public void decode() {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET_KEY))
.withSubject(LOGIN)
.build();
}
How does this work?
Always sign your tokens
The foremost measure to enhance JWT security is to ensure that every JWT you issue is signed. Unsigned tokens are like open books that anyone can
tamper with. Signing your JWTs ensures that any alterations to the tokens after they have been issued can be detected. Most JWT libraries support a
signing function, and using it is usually as simple as providing a secret key when the token is created.
Choose a strong cipher algorithm
It is not enough to merely sign your tokens. You need to sign them with a strong cipher algorithm. Algorithms like HS256 (HMAC using SHA-256) are
considered secure for most purposes. But for an additional layer of security, you could use an algorithm like RS256 (RSA Signature with SHA-256),
which uses a private key for signing and a public key for verification. This way, even if someone gains access to the public key, they will not be
able to forge tokens.
Verify the signature of your tokens
Resolving a vulnerability concerning the validation of JWT token signatures is mainly about incorporating a critical step into your process:
validating the signature every time a token is decoded. Just having a signed token using a secure algorithm is not enough. If you are not validating
signatures, they are not serving their purpose.
Every time your application receives a JWT, it needs to decode the token to extract the information contained within. It is during this decoding
process that the signature of the JWT should also be checked.
To resolve the issue, follow these instructions:
- Use framework-specific functions for signature verification: Most programming frameworks that support JWTs provide specific functions to not
only decode a token but also validate its signature simultaneously. Make sure to use these functions when handling incoming tokens.
- Handle invalid signatures appropriately: If a JWT’s signature does not validate correctly, it means the token is not trustworthy, indicating
potential tampering. The action to take when encountering an invalid token should be denying the request carrying it and logging the event for
further investigation.
- Incorporate signature validation in your tests: When you are writing tests for your application, include tests that check the signature
validation functionality. This can help you catch any instances where signature verification might be unintentionally skipped or bypassed.
By following these practices, you can ensure the security of your application’s JWT handling process, making it resistant to attacks that rely on
tampering with tokens. Validation of the signature needs to be an integral and non-negotiable part of your token handling process.
Going the extra mile
Securely store your secret keys
Ensure that your secret keys are stored securely. They should not be hard-coded into your application code or checked into your version control
system. Instead, consider using environment variables, secure key management systems, or vault services.
Rotate your secret keys
Even with the strongest cipher algorithms, there is a risk that your secret keys may be compromised. Therefore, it is a good practice to
periodically rotate your secret keys. By doing so, you limit the amount of time that an attacker can misuse a stolen key. When you rotate keys, be
sure to allow a grace period where tokens signed with the old key are still accepted to prevent service disruptions.
Resources
Standards