Skip to content

fix(security): escape SQL identifiers, validate order keys, quote the Pgsql DSN and check timezones - #136

Open
BenMacha wants to merge 1 commit into
ccmbenchmark:masterfrom
BenMacha:fix/security-sql-identifiers
Open

BenMacha wants to merge 1 commit into
ccmbenchmark:masterfrom
BenMacha:fix/security-sql-identifiers

Conversation

@BenMacha

Copy link
Copy Markdown
Q A
Bug fix? yes
New feature? no
BC breaks? no (unknown order keys / directions and unknown primary keys now throw ValueException / InvalidArgumentException instead of producing invalid or injectable SQL)
Deprecations? no
Tests pass? yes (to confirm by CI: atoum could not be run locally)
Licence Apache-2.0
Fixed tickets Fixes #134 (follow-up of #120)

Why

While reviewing the drivers after #120 I found other places where a value reaches the SQL text without protection.

What

  • escapeField(): MySQL doubles backticks, PostgreSQL doubles double quotes, SphinxQL (which has no quoting) only accepts plain identifiers. Before, a column name containing the delimiter broke out of it.
  • Repository::getBy($criteria, $forceMaster, $order): order keys were passed to ORDER BY without being checked against the metadata (criteria keys are). They are now resolved like criteria (entity property or column name), an unknown key or a direction other than ASC/DESC throws. LIMIT is cast to int.
  • Repository::get(array $primaries): keys are resolved to columns and must be declared primaries.
  • setTimezone() (both drivers): the value is interpolated with sprintf; it is now validated against a timezone-name pattern.
  • Pgsql\Driver::connect(): every DSN value is quoted, so a password containing a space or a quote no longer breaks the DSN nor injects an option. The redacted DSN in the error message follows.
  • getConnectionKey(): the password is hashed (sha256) instead of being kept in clear text in the pool's array key.
    This key is only used as the index of ConnectionPool::$connections to reuse an open connection; it is never stored, exposed or compared to a known string. Two different passwords still give two different keys and the same password the same key, so the pool behaves exactly as before. Signature and DriverInterface are unchanged: no BC break, nothing to change for users. The gain: a var_dump / profiler / Sentry dump of the pool no longer shows the database password.
  • Mysqli\Driver::quoteValue(): strings are quoted with apostrophes; with sql_mode=ANSI_QUOTES double quotes denote an identifier.
  • Pgsql\Driver::getInsertedId(): throws a QueryException when lastval() fails instead of calling pg_fetch_row(false).

Tests adapted (DSN expectation, apostrophes, order key on a known column) and added (escaping of a delimiter, rejection of an unknown order column and of an invalid direction). CHANGELOG gets an Unreleased section.

… Pgsql DSN and check timezones

- escapeField() doubles backticks (MySQL) and double quotes (PostgreSQL); SphinxQL only accepts plain identifiers
- Repository::getBy() order keys (entity property or column) and direction are checked against the metadata; Repository::get() refuses an unknown primary key
- setTimezone() refuses a value that is not a timezone name; Pgsql connect() quotes every DSN value
- ConnectionPool connection key no longer contains the clear-text password
- Mysqli::execute() quotes inlined strings with apostrophes (safe with sql_mode=ANSI_QUOTES)
- Pgsql::getInsertedId() throws when lastval() fails
@BenMacha
BenMacha requested a review from vgreb as a code owner October 31, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQL identifiers and connection parameters reach the query text without escaping or validation

1 participant