CVE-2021-43608 (published 2021-12-09T20:15Z)

Doctrine DBAL 3.x before 3.1.4 allows SQL Injection. The escaping of offset and length inputs to the generation of a LIMIT clause was not probably cast to an integer, allowing SQL injection to take place if application developers passed unescaped user input to the DBAL QueryBuilder or any other API that ultimately uses the AbstractPlatform::modifyLimitQuery API.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-89
cwe_name: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
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.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/doctrine/dbal
commit_details: repo_url: https://github.com/doctrine/dbal
author: Sergei Morozov
author_date: 2021-11-10 11:42:47-08:00
msg: Cast LIMIT and OFFSET to int when building limit query
repository: repo_url: https://github.com/doctrine/dbal
repo_name: doctrine/dbal
description: Doctrine Database Abstraction Layer
repo_language: PHP
owner: doctrine
file_changes: filename: AbstractPlatform.php
change_type: ModificationType.MODIFY
diff: @@ -3365,7 +3365,11 @@ abstract class AbstractPlatform
             ));
         }
 
-        return $this->doModifyLimitQuery($query, $limit, $offset);
+        if ($limit !== null) {
+            $limit = (int) $limit;
+        }
+
+        return $this->doModifyLimitQuery($query, $limit, (int) $offset);
     }
 
     /**

programming_language: PHP
filename: AbstractPlatformTestCase.php
change_type: ModificationType.MODIFY
diff: @@ -1470,6 +1470,19 @@ abstract class AbstractPlatformTestCase extends TestCase
         );
     }
 
+    public function testLimitOffsetCastToInt(): void
+    {
+        self::assertSame(
+            $this->getLimitOffsetCastToIntExpectedQuery(),
+            $this->platform->modifyLimitQuery('SELECT * FROM user', '1 BANANA', '2 APPLES')
+        );
+    }
+
+    protected function getLimitOffsetCastToIntExpectedQuery(): string
+    {
+        return 'SELECT * FROM user LIMIT 1 OFFSET 2';
+    }
+
     /**
      * @param array<string, mixed> $column
      *

programming_language: PHP
filename: DB2PlatformTest.php
change_type: ModificationType.MODIFY
diff: @@ -773,4 +773,10 @@ class DB2PlatformTest extends AbstractPlatformTestCase
             $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
         );
     }
+
+    protected function getLimitOffsetCastToIntExpectedQuery(): string
+    {
+        return 'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM'
+            . ' FROM (SELECT * FROM user) db21) db22 WHERE db22.DC_ROWNUM >= 3 AND db22.DC_ROWNUM <= 3';
+    }
 }

programming_language: PHP
filename: OraclePlatformTest.php
change_type: ModificationType.MODIFY
diff: @@ -986,4 +986,10 @@ SQL
             ['CHAR(12)', ['length' => 12, 'fixed' => true]],
         ];
     }
+
+    protected function getLimitOffsetCastToIntExpectedQuery(): string
+    {
+        return 'SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT * FROM user) a WHERE ROWNUM <= 3)'
+            . ' WHERE doctrine_rownum >= 3';
+    }
 }

programming_language: PHP