Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Claims are re-used for second JWT #2143

Open
little-apps opened this issue Jul 21, 2021 · 1 comment
Open

Claims are re-used for second JWT #2143

little-apps opened this issue Jul 21, 2021 · 1 comment

Comments

@little-apps
Copy link

Subject of the issue

When one JWT is generated with custom claims and then a second JWT is parsed or generated with no claims, the second JWT contains the claims from the first JWT. This could be considered a security vulnerability because an attacker could set the claims for the second JWT as well as have the second JWT bypass validation checks (as shown below).

Your environment

Q A
Bug? yes?
New Feature? no
Framework Laravel
Framework version 8.50.0
Package version dev-develop (ab00f2d)
PHP version 8.0.7

Steps to reproduce

Generate a JWT from a subject and include custom claims:

use App\Models\User;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Claims\Custom;

$subject = new User(['id' => 1]);

$expiry = 1618263217; // Monday, April 12, 2021 9:33:37 PM

$token1 = JWTAuth::claims([new Custom('exp', $expiry)])->fromSubject($subject);

dump(JWTAuth::setToken($token1)->payload());

Output:

Tymon\JWTAuth\Payload^ {#3588
  -claims: Tymon\JWTAuth\Claims\Collection^ {#3615
    #items: array:8 [
      "iss" => Tymon\JWTAuth\Claims\Issuer^ {#3611
        #name: "iss"
        -value: "http://localhost"
      }
      "iat" => Tymon\JWTAuth\Claims\IssuedAt^ {#3590
        #name: "iat"
        -value: 1626903289
        #leeway: 0
      }
      "exp" => Tymon\JWTAuth\Claims\Custom^ {#3578
        #name: "exp"
        -value: 1618263217
      }
      "nbf" => Tymon\JWTAuth\Claims\NotBefore^ {#3614
        #name: "nbf"
        -value: 1626903289
        #leeway: 0
      }
      "jti" => Tymon\JWTAuth\Claims\JwtId^ {#3598
        #name: "jti"
        -value: "3Fp4N8XctImAdAiS"
      }
      "sub" => Tymon\JWTAuth\Claims\Subject^ {#3613
        #name: "sub"
        -value: null
      }
      "prv" => Tymon\JWTAuth\Claims\Custom^ {#3617
        #name: "prv"
        -value: "23bd5c8949f600adb39e701c400872db7a5976f7"
      }
      "fingerprint" => Tymon\JWTAuth\Claims\Custom^ {#3616
        #name: "fingerprint"
        -value: "0b70e6c4660abb7ec9308c0acb9d91294495c67f41a0bafbc0dafb4a881e901a"
      }
    ]
  }
}

Generate a second JWT with no custom claims:

$token2 = JWTAuth::claims([])->fromSubject($subject);

JWTAuth::setToken($token2)->checkOrFail();

dump(JWTAuth::payload());

Output:

Tymon\JWTAuth\Payload^ {#3629
  -claims: Tymon\JWTAuth\Claims\Collection^ {#3635
    #items: array:8 [
      "iss" => Tymon\JWTAuth\Claims\Issuer^ {#3615
        #name: "iss"
        -value: "http://localhost"
      }
      "iat" => Tymon\JWTAuth\Claims\IssuedAt^ {#3645
        #name: "iat"
        -value: 1626903364
        #leeway: 0
      }
      "exp" => Tymon\JWTAuth\Claims\Custom^ {#3578
        #name: "exp"
        -value: 1618263217
      }
      "nbf" => Tymon\JWTAuth\Claims\NotBefore^ {#3613
        #name: "nbf"
        -value: 1626903364
        #leeway: 0
      }
      "jti" => Tymon\JWTAuth\Claims\JwtId^ {#312
        #name: "jti"
        -value: "gvEpHxlSZPzC00aE"
      }
      "sub" => Tymon\JWTAuth\Claims\Subject^ {#3634
        #name: "sub"
        -value: null
      }
      "prv" => Tymon\JWTAuth\Claims\Custom^ {#3644
        #name: "prv"
        -value: "23bd5c8949f600adb39e701c400872db7a5976f7"
      }
      "fingerprint" => Tymon\JWTAuth\Claims\Custom^ {#3598
        #name: "fingerprint"
        -value: "0b70e6c4660abb7ec9308c0acb9d91294495c67f41a0bafbc0dafb4a881e901a"
      }
    ]
  }
}

Expected behaviour

The checkOrFail() method throws an exception and the claims are set to fresh instances.

Actual behaviour

The checkOrFail() method doesn't throw an exception and the expiry claim is still set to the first claim instance (with value 1618263217).

@Messhias
Copy link

@little-apps thanks for the contribution, but @tymondesigns seems quite absent for this repository and I don't know when he can back to help us, and seems his the only maintainer of the library :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants