CVE-2021-41106 (PHP, published 2021-09-28T21:15Z)
Description: value: JWT is a library to work with JSON Web Token and JSON Web Signature. Prior to versions 3.4.6, 4.0.4, and 4.1.5, users of HMAC-based algorithms (HS256, HS384, and HS512) combined with `Lcobucci\JWT\Signer\Key\LocalFileReference` as key are having their tokens issued/validated using the file path as hashing key - instead of the contents. The HMAC hashing functions take any string as input and, since users can issue and validate tokens, users are lead to believe that everything works properly. Versions 3.4.6, 4.0.4, and 4.1.5 have been patched to always load the file contents, deprecated the `Lcobucci\JWT\Signer\Key\LocalFileReference`, and suggest `Lcobucci\JWT\Signer\Key\InMemory` as the alternative. As a workaround, use `Lcobucci\JWT\Signer\Key\InMemory` instead of `Lcobucci\JWT\Signer\Key\LocalFileReference` to create the instances of one's keys.
Severity: LOW (CVSS v3 base score: 3.3)
Weakness: CWE-345 \u2014 Insufficient Verification of Data Authenticity
The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data.
Repository: https://github.com/lcobucci/jwt
Files changed:
docs/configuration.md
docs/upgrading.md
phpstan.neon.dist
src/Signer/Key/LocalFileReference.php
test/functional/HmacTokenTest.php
test/unit/Signer/EcdsaTest.php
test/unit/Signer/Key/LocalFileReferenceTest.php
test/unit/Signer/RsaTest.php
Commit message: Ensure key contents is used for all hashing algorithms

On v3.4.0 we introduced the
`Lcobucci\JWT\Signer\Key\LocalFileReference`, which was designed to be
used with OpenSSL-based algorithms and avoid having to load the file
contents into user-land to sign/verify tokens.

However, other algorithms don't understand the scheme `file://` and will
incorrectly use the file path as the signing key.

This modifies and deprecates `LocalFileReference` to avoid that
misleading behaviour.

Co-authored-by: Anton Smirnov <sandfox@sandfox.me>
Co-authored-by: Marco Pivetta <ocramius@gmail.com>


Vulnerable code:
use Lcobucci\JWT\Signer\Key\LocalFileReference;
$key = LocalFileReference::file(__DIR__ . '/path-to-my-key-stored-in-a-file.pem'); // this just keeps a reference to file
use Lcobucci\JWT\Signer\Key\LocalFileReference;
    LocalFileReference::file(__DIR__ . '/my-private-key.pem'),
We provide two new implementations: `Lcobucci\JWT\Signer\Key\InMemory` and `Lcobucci\JWT\Signer\Key\LocalFileReference`.
`Lcobucci\JWT\Signer\Key\InMemory` is a drop-in replacement of the behaviour for `Lcobucci\JWT\Signer\Key` in `v3.x`.
        return self::PATH_PREFIX . $this->path;
/** @coversDefaultClass \Lcobucci\JWT\Signer\Key\LocalFileReference */
    /**
     * @test
     *
     * @covers ::file
     * @covers ::__construct
     * @covers ::contents
     */
    public function pathShouldBeNormalised(): void
    {
        $key = LocalFileReference::file('file://' . vfsStream::url('root/test.pem'));

        self::assertSame('file://vfs://root/test.pem', $key->contents());
    }

    public function contentsShouldReturnOnlyTheReferenceToTheFile(): void
        $key = LocalFileReference::file(vfsStream::url('root/test.pem'), 'test');
        self::assertSame('file://vfs://root/test.pem', $key->contents());

Fixed code:
    InMemory::file(__DIR__ . '/my-private-key.pem'),
We provide `Lcobucci\JWT\Signer\Key\InMemory`, a drop-in replacement of the behaviour for `Lcobucci\JWT\Signer\Key` in `v3.x`.
        - '#Call to method .* of deprecated class Lcobucci\\JWT\\Signer\\Key\\LocalFileReference#'
/** @deprecated please use {@see InMemory} instead */
    private string $contents;
        if (! isset($this->contents)) {
            $this->contents = InMemory::file($this->path)->contents();
        }

        return $this->contents;
use Lcobucci\JWT\Signer\Key\LocalFileReference;
use function file_put_contents;
use function is_string;
use function sys_get_temp_dir;
use function tempnam;

    /** @test */
    public function signatureValidationWithLocalFileKeyReferenceWillOperateWithKeyContents(): void
    {
        $key = tempnam(sys_get_temp_dir(), 'key');
        assert(is_string($key));

        file_put_contents($key, 'just a dummy key');

        $validKey      = LocalFileReference::file($key);
        $invalidKey    = InMemory::plainText('file://' . $key);
        $signer        = new Sha256();
        $configuration = Configuration::forSymmetricSigner($signer, $validKey);
        $validator     = $configuration->validator();

        $token = $configuration->builder()
            ->withClaim('foo', 'bar')
            ->getToken($configuration->signer(), $configuration->signingKey());

        self::assertFalse(
            $validator->validate(
                $token,
                new SignedWith($signer, $invalidKey)
            ),
            'Token cannot be validated against the **path** of the key'
        );

        self::assertTrue(
            $validator->validate(
                $token,
                new SignedWith($signer, $validKey)
            ),
            'Token can be validated against the **contents** of the key'
        );
    }
     * @uses \Lcobucci\JWT\Signer\Key\InMemory
     * @uses \Lcobucci\JWT\Signer\Key\InMemory
/**
 * @coversDefaultClass \Lcobucci\JWT\Signer\Key\LocalFileReference
 *
 * @uses \Lcobucci\JWT\Signer\Key\InMemory
 */
    public function pathShouldBeNormalised(): void
        $key = LocalFileReference::file('file://' . vfsStream::url('root/test.pem'), 'test');
        self::assertSame('testing', $key->contents());
     * @uses \Lcobucci\JWT\Signer\Key\InMemory
     * @uses \Lcobucci\JWT\Signer\Key\InMemory
     * @uses \Lcobucci\JWT\Signer\Key\InMemory
     * @uses \Lcobucci\JWT\Signer\Key\InMemory