Skip to content
This repository was archived by the owner on Aug 13, 2026. It is now read-only.

[security] GHSA-j67g-8q5g-jpwc: Fix SQL injection via ORDER BY in GDPR data providers - #1140

Closed
pimcore-deployments wants to merge 1 commit into
2.3from
security/ghsa-j67g-8q5g-jpwc-gdpr-orderby-sqli-a6284b69b4579cfd
Closed

[security] GHSA-j67g-8q5g-jpwc: Fix SQL injection via ORDER BY in GDPR data providers#1140
pimcore-deployments wants to merge 1 commit into
2.3from
security/ghsa-j67g-8q5g-jpwc-gdpr-orderby-sqli-a6284b69b4579cfd

Conversation

@pimcore-deployments

Copy link
Copy Markdown
Collaborator

Vulnerability

GHSA-j67g-8q5g-jpwc — SQL injection via the unsanitized ORDER BY column in the GDPR data-provider search endpoints (GET /admin/gdpr/asset/search-assets, GET /admin/gdpr/data-object/search-data-objects).

Root cause

The request-controlled property field of the sort parameter is extracted by QueryParams::extractSortingSettings() with no validation and handed straight to Doctrine DBAL's QueryBuilder::orderBy():

  • src/GDPR/DataProvider/Assets.php$query->orderBy($sort, $order)
  • src/GDPR/DataProvider/DataObjects.php$query->orderBy($sort, $order)

DBAL's orderBy() concatenates the column name into the SQL string without quoting or binding, so a crafted property (e.g. a CASE WHEN ... SLEEP() ... expression) is injected verbatim into the ORDER BY clause. This enables boolean-blind and time-based blind SQL injection. The sort direction is already whitelisted to ASC/DESC in extractSortingSettings(); only the column was unvalidated — the classic asymmetric-sanitization pattern described in the advisory. Requires an authenticated admin with the gdpr_data_extractor permission.

Fix

Add a shared getValidSortColumn() helper on the common Elements base class that validates the requested column against the actual columns of the queried table (via the DBAL schema manager) before it can reach orderBy(). Any value that is not a real column — every injection payload included — yields null, and the caller then skips ordering entirely. Introspection failures fail closed (return null) for the same reason.

This follows Pimcore's own established getValidTableColumns() convention (see Model/GridConfig/Dao.php et al.) rather than a hardcoded allowlist, so it cannot drift from the schema and continues to allow sorting by any legitimate column. The typesubtype / classnamesubtype mappings are preserved and validated after mapping. Direction handling is left untouched since it was already safe — keeping the change scoped tightly to the vulnerability.

Backward compatibility

Assessed against the Pimcore BC promise. Both Assets and DataObjects (and the Elements base) are annotated @internal, so they are not part of the public API covered by the promise; the new helper is protected on an @internal abstract class. No public signature, return type, service id, or route changes. The only observable behavior change is that a sort column which is not a real table column is now ignored (query runs unordered) instead of being concatenated into the SQL — previously such a value either produced a SQL error or executed injected SQL, so no legitimate consumer relied on it. Sorting by real columns is unchanged.

Tests

Tests added: tests/Model/GDPR/DataProviderSortInjectionTest.php

Covers both providers with (a) an injection payload that is invalid, read-only ORDER BY SQL — it errors against the vulnerable code (fails the test) and is safely rejected against the fix (passes) — and (b) a legitimate id sort that must still succeed, confirming legitimate sorting is preserved. The tests could not be executed in this environment (no Composer/vendor/, no DB, no network); they are written against the standard ModelTestCase suite and lint clean (php -l).

Security-Advisory: pimcore/pimcore/GHSA-j67g-8q5g-jpwc

Generated by Draft a security-advisory fix · opus48 · 237.9 AIC · ⌖ 28.9 AIC · ⊞ 6.2K ·

…A-j67g-8q5g-jpwc)

The GDPR Asset and DataObject search endpoints passed the request-controlled
sort `property` straight to Doctrine DBAL's QueryBuilder::orderBy(), which
concatenates the column name into the SQL without quoting or binding, enabling
boolean-blind and time-based blind SQL injection via the ORDER BY clause.

Validate the sort column against the real columns of the queried table before
it reaches orderBy(), failing closed for any value that is not an actual
column (including injection payloads) and on schema-introspection errors.

Co-Authored-By: Claude <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 New Critical Issues (required ≤ 0)
2 New Major Issues (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@mcop1

mcop1 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

See #1141

@mcop1 mcop1 closed this Jul 30, 2026
@mcop1
mcop1 deleted the security/ghsa-j67g-8q5g-jpwc-gdpr-orderby-sqli-a6284b69b4579cfd branch July 30, 2026 09:56
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants