VYPR
Moderate severityNVD Advisory· Published Nov 24, 2023· Updated Aug 2, 2024

Insertion of Sensitive Information into Log in codeigniter4/shield

CVE-2023-48708

Description

CodeIgniter Shield is an authentication and authorization provider for CodeIgniter 4. In affected versions successful login attempts are recorded with the raw tokens stored in the log table. If a malicious person somehow views the data in the log table they can obtain a raw token which can then be used to send a request with that user's authority. This issue has been addressed in version 1.0.0-beta.8. Users are advised to upgrade. Users unable to upgrade should disable logging for successful login attempts by the configuration files.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
codeigniter4/shieldPackagist
< 1.0.0-beta.81.0.0-beta.8

Affected products

1

Patches

1
7e84c3fb3411

Merge pull request from GHSA-j72f-h752-mx4w

https://github.com/codeigniter4/shieldkenjisNov 22, 2023via ghsa
12 files changed · +174 70
  • docs/addons/jwt.md+26 2 modified
    @@ -34,9 +34,9 @@ To use JWT Authentication, you need additional setup and configuration.
     
         ```php
         <?php
    -    
    +
         // app/Config/AuthJWT.php
    -    
    +
         declare(strict_types=1);
     
         namespace Config;
    @@ -128,6 +128,19 @@ php -r 'echo base64_encode(random_bytes(32));'
     
         The secret key is used for signing and validating tokens.
     
    +### Login Attempt Logging
    +
    +By default, only failed login attempts are recorded in the `auth_token_logins` table.
    +
    +```php
    +public int $recordLoginAttempt = Auth::RECORD_LOGIN_ATTEMPT_FAILURE;
    +```
    +
    +If you don't want any logs, set it to `Auth::RECORD_LOGIN_ATTEMPT_NONE`.
    +
    +If you want to log all login attempts, set it to `Auth::RECORD_LOGIN_ATTEMPT_ALL`.
    +It means you log all requests.
    +
     ## Issuing JWTs
     
     To use JWT Authentication, you need a controller that issues JWTs.
    @@ -351,3 +364,14 @@ It uses the `secret` and `alg` in the `Config\AuthJWT::$keys['default']`.
     It sets the `Config\AuthJWT::$defaultClaims` to the token, and sets
     `"iat"` (Issued At) and `"exp"` (Expiration Time) claims automatically even if
     you don't pass them.
    +
    +## Logging
    +
    +Login attempts are recorded in the `auth_token_logins` table, according to the
    +configuration above.
    +
    +When a failed login attempt is logged, the raw token value sent is saved in
    +the `identifier` column.
    +
    +When a successful login attempt is logged, the SHA256 hash value of the token
    +sent is saved in the `identifier` column.
    
  • docs/getting_started/configuration.md+3 15 modified
    @@ -8,20 +8,8 @@ If you have completed the setup according to this documentation, you will have
     the following configuration files:
     
     - **app/Config/Auth.php**
    -- **app/Config/AuthGroups.php** - For Authorization
    -- **app/Config/AuthToken.php** - For AccessTokens and HmacSha256 Authentication
    -- **app/Config/AuthJWT.php** - For JWT Authentication
    +- **app/Config/AuthGroups.php** - For [Authorization](../references/authorization.md)
    +- **app/Config/AuthToken.php** - For [AccessTokens](../references/authentication/tokens.md#configuration) and [HmacSha256](../references/authentication/hmac.md#configuration) Authentication
    +- **app/Config/AuthJWT.php** - For [JWT Authentication](../addons/jwt.md#configuration)
     
     Note that you do not need to have configuration files for features you do not use.
    -
    -This section describes the major Config items that are not described elsewhere.
    -
    -## AccessTokens Authenticator
    -
    -### Access Token Lifetime
    -
    -By default, Access Tokens can be used for 1 year since the last use. This can be easily modified in the **app/Config/AuthToken.php** config file.
    -
    -```php
    -public int $unusedTokenLifetime = YEAR;
    -```
    
  • docs/references/authentication/hmac.md+47 14 modified
    @@ -112,20 +112,6 @@ $token = $user->getHmacTokenById($id);
     $tokens = $user->hmacTokens();
     ```
     
    -## HMAC Keys Lifetime
    -
    -HMAC Keys/Tokens will expire after a specified amount of time has passed since they have been used.
    -This uses the same configuration value as AccessTokens.
    -
    -By default, this is set to 1 year. You can change this value by setting the `$unusedTokenLifetime`
    -value in the **app/Config/AuthToken.php** config file. This is in seconds so that you can use the
    -[time constants](https://codeigniter.com/user_guide/general/common_functions.html#time-constants)
    -that CodeIgniter provides.
    -
    -```php
    -public $unusedTokenLifetime = YEAR;
    -```
    -
     ## HMAC Keys Scopes
     
     Each token (set of keys) can be given one or more scopes they can be used within. These can be thought of as
    @@ -219,3 +205,50 @@ authtoken.hmacEncryptionCurrentKey = k2
     Depending on the set length of the Secret Key and the type of encryption used, it is possible for the encrypted value to
     exceed the database column character limit of 255 characters. If this happens, creation of a new HMAC identity will
     throw a `RuntimeException`.
    +
    +## Configuration
    +
    +Configure **app/Config/AuthToken.php** for your needs.
    +
    +!!! note
    +
    +    Shield does not expect you use the Access Token Authenticator and HMAC Authenticator
    +    at the same time. Therefore, some Config items are common.
    +
    +### HMAC Keys Lifetime
    +
    +HMAC Keys/Tokens will expire after a specified amount of time has passed since they have been used.
    +
    +By default, this is set to 1 year. You can change this value by setting the `$unusedTokenLifetime`
    +value. This is in seconds so that you can use the
    +[time constants](https://codeigniter.com/user_guide/general/common_functions.html#time-constants)
    +that CodeIgniter provides.
    +
    +```php
    +public $unusedTokenLifetime = YEAR;
    +```
    +
    +### Login Attempt Logging
    +
    +By default, only failed login attempts are recorded in the `auth_token_logins` table.
    +This can be modified by changing the `$recordLoginAttempt` value.
    +
    +```php
    +public int $recordLoginAttempt = Auth::RECORD_LOGIN_ATTEMPT_FAILURE;
    +```
    +
    +If you don't want any logs, set it to `Auth::RECORD_LOGIN_ATTEMPT_NONE`.
    +
    +If you want to log all login attempts, set it to `Auth::RECORD_LOGIN_ATTEMPT_ALL`.
    +It means you log all requests.
    +
    +## Logging
    +
    +Login attempts are recorded in the `auth_token_logins` table, according to the
    +configuration above.
    +
    +When a failed login attempt is logged, the raw token value sent is saved in
    +the `identifier` column.
    +
    +When a successful login attempt is logged, the token name is saved in the
    +`identifier` column.
    
  • docs/references/authentication/tokens.md+49 12 modified
    @@ -83,18 +83,6 @@ $token = $user->getAccessTokenById($id);
     $tokens = $user->accessTokens();
     ```
     
    -## Access Token Lifetime
    -
    -Tokens will expire after a specified amount of time has passed since they have been used.
    -By default, this is set to 1 year. You can change this value by setting the `$unusedTokenLifetime`
    -value in the **app/Config/AuthToken.php** config file. This is in seconds so that you can use the
    -[time constants](https://codeigniter.com/user_guide/general/common_functions.html#time-constants)
    -that CodeIgniter provides.
    -
    -```php
    -public $unusedTokenLifetime = YEAR;
    -```
    -
     ## Access Token Scopes
     
     Each token can be given one or more scopes they can be used within. These can be thought of as
    @@ -125,3 +113,52 @@ if ($user->tokenCant('forums.manage')) {
         // do something....
     }
     ```
    +
    +## Configuration
    +
    +Configure **app/Config/AuthToken.php** for your needs.
    +
    +!!! note
    +
    +    Shield does not expect you use the Access Token Authenticator and HMAC Authenticator
    +    at the same time. Therefore, some Config items are common.
    +
    +### Access Token Lifetime
    +
    +Tokens will expire after a specified amount of time has passed since they have been used.
    +
    +By default, this is set to 1 year.
    +You can change this value by setting the `$unusedTokenLifetime` value. This is
    +in seconds so that you can use the
    +[time constants](https://codeigniter.com/user_guide/general/common_functions.html#time-constants)
    +that CodeIgniter provides.
    +
    +```php
    +public $unusedTokenLifetime = YEAR;
    +```
    +
    +### Login Attempt Logging
    +
    +By default, only failed login attempts are recorded in the `auth_token_logins` table.
    +
    +This can be modified by changing the `$recordLoginAttempt` value.
    +
    +```php
    +public int $recordLoginAttempt = Auth::RECORD_LOGIN_ATTEMPT_FAILURE;
    +```
    +
    +If you don't want any logs, set it to `Auth::RECORD_LOGIN_ATTEMPT_NONE`.
    +
    +If you want to log all login attempts, set it to `Auth::RECORD_LOGIN_ATTEMPT_ALL`.
    +It means you log all requests.
    +
    +## Logging
    +
    +Login attempts are recorded in the `auth_token_logins` table, according to the
    +configuration above.
    +
    +When a failed login attempt is logged, the raw token value sent is saved in
    +the `identifier` column.
    +
    +When a successful login attempt is logged, the token name is saved in the
    +`identifier` column.
    
  • phpstan-baseline.php+0 5 modified
    @@ -361,11 +361,6 @@
     	'count' => 1,
     	'path' => __DIR__ . '/src/Models/UserModel.php',
     ];
    -$ignoreErrors[] = [
    -	'message' => '#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with \'CodeIgniter\\\\\\\\Shield\\\\\\\\Result\' and CodeIgniter\\\\Shield\\\\Result will always evaluate to true\\.$#',
    -	'count' => 2,
    -	'path' => __DIR__ . '/tests/Authentication/Authenticators/AccessTokenAuthenticatorTest.php',
    -];
     $ignoreErrors[] = [
     	'message' => '#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) with \'CodeIgniter\\\\\\\\Shield\\\\\\\\Result\' and CodeIgniter\\\\Shield\\\\Result will always evaluate to true\\.$#',
     	'count' => 3,
    
  • src/Authentication/Authenticators/AccessTokens.php+5 6 modified
    @@ -77,14 +77,15 @@ public function attempt(array $credentials): Result
                 return $result;
             }
     
    -        $user = $result->extraInfo();
    +        $user  = $result->extraInfo();
    +        $token = $user->getAccessToken($this->getBearerToken());
     
             if ($user->isBanned()) {
                 if ($config->recordLoginAttempt >= Auth::RECORD_LOGIN_ATTEMPT_FAILURE) {
                     // Record a banned login attempt.
                     $this->loginModel->recordLoginAttempt(
                         self::ID_TYPE_ACCESS_TOKEN,
    -                    $credentials['token'] ?? '',
    +                    $token->name ?? '',
                         false,
                         $ipAddress,
                         $userAgent,
    @@ -100,17 +101,15 @@ public function attempt(array $credentials): Result
                 ]);
             }
     
    -        $user = $user->setAccessToken(
    -            $user->getAccessToken($this->getBearerToken())
    -        );
    +        $user = $user->setAccessToken($token);
     
             $this->login($user);
     
             if ($config->recordLoginAttempt === Auth::RECORD_LOGIN_ATTEMPT_ALL) {
                 // Record a successful login attempt.
                 $this->loginModel->recordLoginAttempt(
                     self::ID_TYPE_ACCESS_TOKEN,
    -                $credentials['token'] ?? '',
    +                $token->name ?? '',
                     true,
                     $ipAddress,
                     $userAgent,
    
  • src/Authentication/Authenticators/HmacSha256.php+5 6 modified
    @@ -78,14 +78,15 @@ public function attempt(array $credentials): Result
                 return $result;
             }
     
    -        $user = $result->extraInfo();
    +        $user  = $result->extraInfo();
    +        $token = $user->getHmacToken($this->getHmacKeyFromToken());
     
             if ($user->isBanned()) {
                 if ($config->recordLoginAttempt >= Auth::RECORD_LOGIN_ATTEMPT_FAILURE) {
                     // Record a banned login attempt.
                     $this->loginModel->recordLoginAttempt(
                         self::ID_TYPE_HMAC_TOKEN,
    -                    $credentials['token'] ?? '',
    +                    $token->name ?? '',
                         false,
                         $ipAddress,
                         $userAgent,
    @@ -101,17 +102,15 @@ public function attempt(array $credentials): Result
                 ]);
             }
     
    -        $user = $user->setHmacToken(
    -            $user->getHmacToken($this->getHmacKeyFromToken())
    -        );
    +        $user = $user->setHmacToken($token);
     
             $this->login($user);
     
             if ($config->recordLoginAttempt === Auth::RECORD_LOGIN_ATTEMPT_ALL) {
                 // Record a successful login attempt.
                 $this->loginModel->recordLoginAttempt(
                     self::ID_TYPE_HMAC_TOKEN,
    -                $credentials['token'] ?? '',
    +                $token->name ?? '',
                     true,
                     $ipAddress,
                     $userAgent,
    
  • src/Authentication/Authenticators/JWT.php+2 2 modified
    @@ -103,7 +103,7 @@ public function attempt(array $credentials): Result
                     // Record a banned login attempt.
                     $this->tokenLoginModel->recordLoginAttempt(
                         self::ID_TYPE_JWT,
    -                    $credentials['token'] ?? '',
    +                    'sha256:' . hash('sha256', $credentials['token'] ?? ''),
                         false,
                         $ipAddress,
                         $userAgent,
    @@ -125,7 +125,7 @@ public function attempt(array $credentials): Result
                 // Record a successful login attempt.
                 $this->tokenLoginModel->recordLoginAttempt(
                     self::ID_TYPE_JWT,
    -                $credentials['token'] ?? '',
    +                'sha256:' . hash('sha256', $credentials['token']),
                     true,
                     $ipAddress,
                     $userAgent,
    
  • src/Config/AuthToken.php+1 1 modified
    @@ -46,7 +46,7 @@ class AuthToken extends BaseAuthToken
     
         /**
          * --------------------------------------------------------------------
    -     * Unused Token Lifetime
    +     * Unused Token Lifetime for Token Auth and HMAC Auth
          * --------------------------------------------------------------------
          * Determines the amount of time, in seconds, that an unused token can
          * be used.
    
  • tests/Authentication/Authenticators/AccessTokenAuthenticatorTest.php+32 3 modified
    @@ -17,11 +17,11 @@
     use CodeIgniter\Shield\Authentication\Authentication;
     use CodeIgniter\Shield\Authentication\Authenticators\AccessTokens;
     use CodeIgniter\Shield\Config\Auth;
    +use CodeIgniter\Shield\Config\AuthToken;
     use CodeIgniter\Shield\Entities\AccessToken;
     use CodeIgniter\Shield\Entities\User;
     use CodeIgniter\Shield\Models\UserIdentityModel;
     use CodeIgniter\Shield\Models\UserModel;
    -use CodeIgniter\Shield\Result;
     use CodeIgniter\Test\Mock\MockEvents;
     use Config\Services;
     use Tests\Support\DatabaseTestCase;
    @@ -182,7 +182,6 @@ public function testAttemptCannotFindUser(): void
                 'token' => 'abc123',
             ]);
     
    -        $this->assertInstanceOf(Result::class, $result);
             $this->assertFalse($result->isOK());
             $this->assertSame(lang('Auth.badToken'), $result->reason());
     
    @@ -205,7 +204,6 @@ public function testAttemptSuccess(): void
                 'token' => $token->raw_token,
             ]);
     
    -        $this->assertInstanceOf(Result::class, $result);
             $this->assertTrue($result->isOK());
     
             $foundUser = $result->extraInfo();
    @@ -222,6 +220,37 @@ public function testAttemptSuccess(): void
             ]);
         }
     
    +    public function testAttemptSuccessLog(): void
    +    {
    +        // Change $recordLoginAttempt in Config.
    +        /** @var AuthToken $config */
    +        $config                     = config('AuthToken');
    +        $config->recordLoginAttempt = Auth::RECORD_LOGIN_ATTEMPT_ALL;
    +
    +        /** @var User $user */
    +        $user  = fake(UserModel::class);
    +        $token = $user->generateAccessToken('foo');
    +        $this->setRequestHeader($token->raw_token);
    +
    +        $result = $this->auth->attempt([
    +            'token' => $token->raw_token,
    +        ]);
    +
    +        $this->assertTrue($result->isOK());
    +
    +        $foundUser = $result->extraInfo();
    +        $this->assertInstanceOf(User::class, $foundUser);
    +        $this->assertSame($user->id, $foundUser->id);
    +        $this->assertInstanceOf(AccessToken::class, $foundUser->currentAccessToken());
    +        $this->assertSame($token->token, $foundUser->currentAccessToken()->token);
    +
    +        $this->seeInDatabase($this->tables['token_logins'], [
    +            'id_type'    => AccessTokens::ID_TYPE_ACCESS_TOKEN,
    +            'identifier' => 'foo',
    +            'success'    => 1,
    +        ]);
    +    }
    +
         protected function setRequestHeader(string $token): void
         {
             $request = service('request');
    
  • tests/Authentication/Authenticators/HmacAuthenticatorTest.php+2 2 modified
    @@ -273,7 +273,7 @@ public function testAttemptSuccess(): void
             // A login attempt should have been recorded
             $this->seeInDatabase($this->tables['token_logins'], [
                 'id_type'    => HmacSha256::ID_TYPE_HMAC_TOKEN,
    -            'identifier' => $rawToken,
    +            'identifier' => 'foo',
                 'success'    => 1,
             ]);
     
    @@ -310,7 +310,7 @@ public function testAttemptBanned(): void
             // A login attempt should have been recorded
             $this->seeInDatabase($this->tables['token_logins'], [
                 'id_type'    => HmacSha256::ID_TYPE_HMAC_TOKEN,
    -            'identifier' => $rawToken,
    +            'identifier' => 'foo',
                 'success'    => 0,
             ]);
         }
    
  • tests/Authentication/Authenticators/JWTAuthenticatorTest.php+2 2 modified
    @@ -226,7 +226,7 @@ public function testAttemptBannedUser(): void
             // The login attempt should have been recorded
             $this->seeInDatabase('auth_token_logins', [
                 'id_type'    => JWT::ID_TYPE_JWT,
    -            'identifier' => $token,
    +            'identifier' => 'sha256:' . hash('sha256', $token),
                 'success'    => 0,
                 'user_id'    => $this->user->id,
             ]);
    @@ -256,7 +256,7 @@ public function testAttemptSuccess(): void
             // A login attempt should have been recorded
             $this->seeInDatabase('auth_token_logins', [
                 'id_type'    => JWT::ID_TYPE_JWT,
    -            'identifier' => $token,
    +            'identifier' => 'sha256:' . hash('sha256', $token),
                 'success'    => 1,
             ]);
         }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.