CVE-2022-35947: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: GLPI stands for Gestionnaire Libre de Parc Informatique and is a Free Asset and IT Management Software package, that provides ITIL Service Desk features, licenses tracking and software auditing. Affected versions have been found to be vulnerable to a SQL injection attack which an attacker could leverage to simulate an arbitrary user login. Users are advised to upgrade to version 10.0.3. Users unable to upgrade should disable the `Enable login with external token` API configuration.
CWE description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
CVSS v3 base score: 9.8
Published: 2022-09-14T18:15Z
Repository: https://github.com/glpi-project/glpi
Language: PHP
Files changed: src/User.php, tests/functionnal/User.php
Commit date: 2022-09-14 13:54:36 +0200
Commit message: Ensure token based authentication only accept strings

Diff: diff --git a/src/User.php b/src/User.php
index 6dbbc30bf0dc..26589729f548 100644
--- a/src/User.php
+++ b/src/User.php
@@ -625,10 +625,18 @@ class User extends CommonDBTM
      *
      * @return boolean
      */
     public function getFromDBbyToken($token, $field = 'personal_token')
     {
+        if (!is_string($token)) {
+            trigger_error(
+                sprintf('Unexpected token value received: "string" expected, received "%s".', gettype($token)),
+                E_USER_WARNING
+            );
+            return false;
+        }
+
         $fields = ['personal_token', 'api_token'];
         if (!in_array($field, $fields)) {
             trigger_error(
                 'User::getFromDBbyToken() can only be called with $field parameter with theses values: \'' . implode('\', \'', $fields) . '\'',
                 E_USER_WARNING
diff --git a/tests/functionnal/User.php b/tests/functionnal/User.php
index 8a4070217cd2..4f21ce674c5b 100644
--- a/tests/functionnal/User.php
+++ b/tests/functionnal/User.php
@@ -171,17 +171,26 @@ class User extends \DbTestCase
         $user2 = new \User();
         $this->boolean($user2->getFromDBbyToken($token))->isTrue();
         $this->array($user2->fields)->isIdenticalTo($user->fields);
 
         $this->when(
-            function () use ($uid) {
-                $this->testedInstance->getFromDBbyToken($uid, 'my_field');
+            function () {
+                $this->testedInstance->getFromDBbyToken('1485dd60301311eda2610242ac12000249aef69a', 'my_field');
             }
         )->error
-         ->withType(E_USER_WARNING)
-         ->withMessage('User::getFromDBbyToken() can only be called with $field parameter with theses values: \'personal_token\', \'api_token\'')
-         ->exists();
+            ->withType(E_USER_WARNING)
+            ->withMessage('User::getFromDBbyToken() can only be called with $field parameter with theses values: \'personal_token\', \'api_token\'')
+            ->exists();
+
+        $this->when(
+            function () {
+                $this->testedInstance->getFromDBbyToken(['REGEX', '.*'], 'api_token');
+            }
+        )->error()
+            ->withType(E_USER_WARNING)
+            ->withMessage('Unexpected token value received: "string" expected, received "array".')
+            ->exists();
     }
 
     public function testPrepareInputForAdd()
     {
         $this->login();

Vulnerable code:             function () use ($uid) {
                $this->testedInstance->getFromDBbyToken($uid, 'my_field');
         ->withType(E_USER_WARNING)
         ->withMessage('User::getFromDBbyToken() can only be called with $field parameter with theses values: \'personal_token\', \'api_token\'')
         ->exists();
Fixed code:         if (!is_string($token)) {
            trigger_error(
                sprintf('Unexpected token value received: "string" expected, received "%s".', gettype($token)),
                E_USER_WARNING
            );
            return false;
        }

            function () {
                $this->testedInstance->getFromDBbyToken('1485dd60301311eda2610242ac12000249aef69a', 'my_field');
            ->withType(E_USER_WARNING)
            ->withMessage('User::getFromDBbyToken() can only be called with $field parameter with theses values: \'personal_token\', \'api_token\'')
            ->exists();

        $this->when(
            function () {
                $this->testedInstance->getFromDBbyToken(['REGEX', '.*'], 'api_token');
            }
        )->error()
            ->withType(E_USER_WARNING)
            ->withMessage('Unexpected token value received: "string" expected, received "array".')
            ->exists();