phpjwtdecodeencode

PHP JWT not verified other than PHP


I am using PHP firebase JWT for implementation of JWT in my PHP app. Here is my code

    $payload = [
        'iss' => 'http://test.com',  
        'aud' => 'http://test.com',  
        'iat' => time(),  // Issued at
        'exp' => time() + 3600,  // Expiration (1 hour)
        'data' => [
            'user_id' => $user['id'],
            'email' => $user['email'],
            'role' => $user['role'],
            'is_admin' => $user['isadmin']
        ]
    ];
    $jwt = JWT::encode($payload, $key, 'HS256', $key_file);

After encoding it generates the token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Ii92YXIvd3d3L215a2V5LmtleSJ9.eyJpc3MiOiJodHRwOi8vdGVzdC5jb20iLCJhdWQiOiJodHRwOi8vdGVzdC5jb20iLCJpYXQiOjE3MjM5NzQ5OTMsImV4cCI6MTcyMzk3ODU5MywiZGF0YSI6eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImFiYyIsInJvbGUiOiJ1c2VyIiwiaXNfYWRtaW4iOjB9fQ.uC13pivSZGnPr6i8zmPCfEFMsykWR5miIK8t0-DCnug which is fine

The content of key file are hellohellohellohellohellohellohe I used the website https://jwt.io/ to test my token and several other websites https://dinochiesa.github.io/jwt/

Now I want a normal user to generate a token through any website by changing the key kid parameter and using any of the website on the internet, so it can be used on the site (demonstrating a vulnerability).

I am facing two issues; if I try to verify the token with the above key on any of the websites with the key file, it says it cannot match or verify; what is the issue? Does PHP Firebase based JWT works differently? I also checked this one JWT signature not verifying in PHP


Solution

  • Because you're reading the key from a file, rather than testing with a hard-coded value, it's hard to see if there are extra characters included, such as line breaks.

    If you generate the hash manually with what you thought was your key, you will see a different signature:

    $msg = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Ii92YXIvd3d3L215a2V5LmtleSJ9.eyJpc3MiOiJodHRwOi8vdGVzdC5jb20iLCJhdWQiOiJodHRwOi8vdGVzdC5jb20iLCJpYXQiOjE3MjM5NzQ5OTMsImV4cCI6MTcyMzk3ODU5MywiZGF0YSI6eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImFiYyIsInJvbGUiOiJ1c2VyIiwiaXNfYWRtaW4iOjB9fQ';
    $key = 'hellohellohellohellohellohellohe';
    $algorithm = 'SHA256';
    
    $signature = hash_hmac($algorithm, $msg, $key, true);
    echo base64_encode($signature);
    
    #=> zBeba1d7bk/OhMeo5P/Aw1aO2v4JxW+8QXGJsCx1AWI=
    

    But if you add a single newline on the end, you will see the signature in your sample JWT (apart from the slightly different variant of BASE64 encoding, with "-" vs "+"):

    $msg = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Ii92YXIvd3d3L215a2V5LmtleSJ9.eyJpc3MiOiJodHRwOi8vdGVzdC5jb20iLCJhdWQiOiJodHRwOi8vdGVzdC5jb20iLCJpYXQiOjE3MjM5NzQ5OTMsImV4cCI6MTcyMzk3ODU5MywiZGF0YSI6eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImFiYyIsInJvbGUiOiJ1c2VyIiwiaXNfYWRtaW4iOjB9fQ';
    $key = "hellohellohellohellohellohellohe\n";
    $algorithm = 'SHA256';
    
    $signature = hash_hmac($algorithm, $msg, $key, true);
    echo base64_encode($signature);
    
    #=> uC13pivSZGnPr6i8zmPCfEFMsykWR5miIK8t0+DCnug=
    

    You could do the same experiment directly with the JWT::encode function, but I don't have an environment to hand where I can install the library.