0

I have implemented the JWT in my spring boot application with the help of This Artical

Here my issue is when I am testing with postman, I need to give the JWT token in headers, I have passed the same to my get method, I am getting the response, When I try to change the last letter of JWT token, then also I am getting the response 200 and getting the data. It means somewhere the JWT implementation getting wrong.

Token is something like below eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImU3MjcyNzY4LTNiYWYtNGNlMi1iYWI4LWMzMmRmM2M0OTEwNiIsImlhdCI6MTYwMDc4NDgyOCwiZXhwIjoxNjAwNzg4NDI4fQ.FVQDZW9chTpmBQhGKBhMlUYz9qTUdh7IRZnVmP7aSn

If I change more than one character at the last of JWT token, then it is giving 401 Unauthorized. If I give only w word at the last of JWT token, then also I am getting 401, When I give other than w then I am getting 200

How can I resolve the same. When I chang any one character then it should identify the change it should give 401

import java.io.Serializable;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtTokenUtil implements Serializable {

    private static final long serialVersionUID = -2550185165626007488L;
    
    public static final long JWT_TOKEN_VALIDITY = 5*60*60;

    @Value("${jwt.secret}")
    private String secret;

    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    public Date getIssuedAtDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getIssuedAt);
    }

    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        String encodedString = Base64.getEncoder().encodeToString(secret.getBytes());
        return Jwts.parser().setSigningKey(encodedString).parseClaimsJws(token).getBody();
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Boolean ignoreTokenExpiration(String token) {
        // here you specify tokens, for that the expiration is ignored
        return false;
    }

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return doGenerateToken(claims, userDetails.getUsername());
    }

    private String doGenerateToken(Map<String, Object> claims, String subject) {
        
        String encodedString = Base64.getEncoder().encodeToString(secret.getBytes());
        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY*1000)).signWith(SignatureAlgorithm.HS512, encodedString).compact();
    }
    
    public Boolean canTokenBeRefreshed(String token) {
        return (!isTokenExpired(token) || ignoreTokenExpiration(token));
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
}
Jeb
  • 447
  • 2
  • 6
  • 13
  • can you verify the length of jwt-secret that you are using RFC7518 standard states that "A key of the same size as the hash output – Muhammad Usama Ashraf Sep 22 '20 at 14:45
  • @MuhammadUsamaAshraf my intention, When the `JWT` last word I will replace with another character then also it is giving the correct output, but it should not be. let me know If I am confusing you – Jeb Sep 22 '20 at 14:57
  • JWT Consist of Three Parts HEADER.PAYLOAD.SIGNATURE , PAYLOAD is only encrypted Text , HEADER Contain the Information of the encryption you are using, SIGNATURE is made by JWT-Secret and has to be verified by backend , if you use week secret it can verify the signature also that are not made by the secret – Muhammad Usama Ashraf Sep 22 '20 at 15:05
  • In Your JWT Token Header: `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9` , Payload: `eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImU3MjcyNzY4LTNiYWYtNGNlMi1iYWI4LWMzMmRmM2M0OTEwNiIsImlhdCI6MTYwMDc4NDgyOCwiZXhwIjoxNjAwNzg4NDI4fQ` , Signature : `FVQDZW9chTpmBQhGKBhMlUYz9qTUdh7IRZnVmP7aSn` if you use week secret it can verify the signature also that are similar – Muhammad Usama Ashraf Sep 22 '20 at 15:08
  • @jps it seems near to my requirement, can we make it work with the correct JWT only – Jeb Sep 23 '20 at 02:26
  • @jps is it possible to prevent the jwt token, when I change the jwt it should trow 401 error – Jeb Sep 23 '20 at 04:47
  • 1
    @Jeb: I wonder why this is a problem for you. The hash value itself is not affected, it's only the last 2 insignificant bits in the base64 encoding. If it's really important for you, change to HS384. Then you have a 348 bit (48 byte) hash, which is represented in 64 Base64 characters. 384 can be divided by 8 and by 6 without rest, so there are no insignificant bits on the end and any change will cause a failed verification. – jps Sep 23 '20 at 08:42
  • I also added this solution (using HS384) to the linked answer. You can approve the proposed q/a, so that you post is closed as a duplicate. – jps Sep 23 '20 at 13:23

0 Answers0