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

[security] GHSA-g8pm-xq73-xqq8: Fix blind SQL injection in GDPR search ORDER BY - #1141

Closed
pimcore-deployments wants to merge 1 commit into
2.3from
security/ghsa-g8pm-xq73-xqq8-6903204fc6bada8c
Closed

[security] GHSA-g8pm-xq73-xqq8: Fix blind SQL injection in GDPR search ORDER BY#1141
pimcore-deployments wants to merge 1 commit into
2.3from
security/ghsa-g8pm-xq73-xqq8-6903204fc6bada8c

Conversation

@pimcore-deployments

Copy link
Copy Markdown
Collaborator

Vulnerability

Blind SQL injection (CWE-89) in the GDPR search endpoints GET /admin/gdpr/data-object/search-data-objects and GET /admin/gdpr/asset/search-assets. An authenticated user with the low-privilege gdpr_data_extractor permission controls the sort[].property value, which QueryParams::extractSortingSettings() returns as-is (only remapping classname/type to subtype, with no field allowlist).

Root cause: that attacker-controlled property was passed directly into the ORDER BY clause:

$query->orderBy($sort, $order); // $sort unquoted, unvalidated

in src/GDPR/DataProvider/DataObjects.php and src/GDPR/DataProvider/Assets.php. Identifiers in ORDER BY cannot be bound as parameters, and the WHERE id = :id clause is bound separately, so the sort value is a working injection sink independent of the filter. It escalates to a full read of the database (e.g. users password hashes) via an error-based extractvalue payload, since MariaDB evaluates the ORDER BY expression even for a single-row result.

This is a new, previously unpatched sink, distinct from the already-fixed filter/date SQL-injection paths.

Fix

The sort identifier is now quoted with \Pimcore\Db::get()->quoteIdentifier() before being placed into ORDER BY, in both data providers:

$query->orderBy($db->quoteIdentifier($sort), $order);

Quoting escapes any embedded backticks and wraps the value as a single identifier, so an injected expression such as extractvalue(...) or (SELECT 1) becomes an unknown-column reference the database rejects rather than SQL it executes. This matches the existing convention in this repository — QueryParams::getFilterCondition() already uses quoteIdentifier() for dynamic identifiers — and the Pimcore coding guideline that dynamic column names which cannot be bound must be passed through quoteIdentifier(). The sort direction ($order) was already constrained to ASC/DESC by extractSortingSettings() and is unchanged.

Legitimate sorting is preserved: real column names (id, type, subtype, filename, ...) quote to themselves and sort as before; only values that are not valid identifiers are now rejected.

The sibling GDPR provider PimcoreUsers is not affected: it sorts via User\Listing::setOrderKey(), which handles identifier quoting in the listing DAO, not through a raw orderBy() call. The other extractSortingSettings() callers reach the ORM listing layer rather than a hand-built query builder.

Backward compatibility

No BC break. Both DataObjects and Assets are annotated @internal, so they are outside the BC promise; the public searchData() signature is unchanged, and legitimate column sorts return identical results. The only behavioral change is that malformed/malicious sort identifiers now raise a DB error instead of being executed — the intended security effect.

Tests

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

The test exercises both providers. The injection cases pass (SELECT 1) as the sort property (valid SQL, not a column): against the vulnerable code it is emitted as ORDER BY (SELECT 1) and executes without error, so the expected exception is never thrown and the test fails; against the fix it is quoted to ORDER BY `(SELECT 1)` and rejected as an unknown column, so the test passes. Two companion cases assert that legitimate sorts (id, filename) still succeed, confirming valid behavior is retained.

The tests could not be executed in this environment (no Composer install / DB available in the sandbox); they are written to run under the existing Codeception Model suite, which provides the DB connection the query builder needs.

Security-Advisory: pimcore/pimcore/GHSA-g8pm-xq73-xqq8

Generated by Draft a security-advisory fix · opus48 · 195.3 AIC · ⌖ 32.1 AIC · ⊞ 6.2K ·

…xqq8)

Quote the sort identifier before it is placed into the ORDER BY clause of
the GDPR data-object and asset search queries. The value comes from the
ExtJS `sort` parameter via QueryParams::extractSortingSettings with no
allowlist; as an ORDER BY identifier it cannot be bound as a parameter, so
it is now quoted with quoteIdentifier() to neutralize injection.

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

Copy link
Copy Markdown

@mcop1

mcop1 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Moved to the enterprise repo: pimcore/ee-admin-ui-classic-bundle#391 (base 2.3, same advisory GHSA-g8pm-xq73-xqq8).

The commit was cherry-picked unchanged — identical diff, original authorship preserved. Closing here in favour of that PR.

@mcop1 mcop1 closed this Jul 30, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 30, 2026
@mcop1
mcop1 deleted the security/ghsa-g8pm-xq73-xqq8-6903204fc6bada8c branch July 30, 2026 10:17
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.

2 participants