Skip to content

🚨 [security] [php] Update predis/predis 3.0.1 → 3.6.0 (minor) - #147

Open
depfu[bot] wants to merge 1 commit into
mainfrom
depfu/update/composer/predis/predis-3.6.0
Open

🚨 [security] [php] Update predis/predis 3.0.1 → 3.6.0 (minor)#147
depfu[bot] wants to merge 1 commit into
mainfrom
depfu/update/composer/predis/predis-3.6.0

Conversation

@depfu

@depfu depfu Bot commented Sep 8, 2026

Copy link
Copy Markdown

🚨 Your current dependencies have known security vulnerabilities 🚨

This dependency update fixes known security vulnerabilities. Please see the details below and assess their impact carefully. We recommend to merge and deploy this as soon as possible!


Here is everything you need to know about this update. Please take a good look at what changed and the test results before merging this pull request.

What changed?

✳️ predis/predis (3.0.1 → 3.6.0) · Repo · Changelog

Security Advisories 🚨

🚨 Predis: Redis command injection and denial of service via CRLF smuggling in pipelined commands on aggregate connections

Summary

An improper CRLF neutralization flaw in Predis' pipeline handling on
aggregate connections lets an unauthenticated attacker who can influence any
pipelined argument — a value or a key, e.g. a URL slug used as a cache key —
smuggle arbitrary Redis commands into the connection.

  • On cluster connections (cluster option, incl. client-side sharding) this
    is remote command injection: shard-wide FLUSHDB, targeted DEL/SET,
    same-slot key theft via GET, cache poisoning, and possible node/cluster
    outage.
  • On replication connections (replication option) it is a reliable,
    repeatable denial of service (uncaught fatal error) triggered by any value
    containing \r\n.

Details

When a pipeline is executed over an aggregate connection,
AbstractAggregateConnection::write() re-parses the already-serialized pipeline
buffer with explode("\r\n") instead of honoring RESP length prefixes:

RESP is length-prefixed, so the Redis server parses the original stream
correctly — but this second, client-side parser treats attacker-controlled
\r\n sequences as command boundaries. An argument such as:

PAD\r\n*1\r\n$7\r\nFLUSHDB

is a single data value to the server, but a complete, valid FLUSHDB command to
the re-parser. The consequence depends on the connection type:

  • Replication: a pipeline forces switchToMaster(), so all chunks go to the
    master and the byte stream stays intact — but the misaligned chunk makes
    deserializeCommand() throw an uncaught UnexpectedValueException: Invalid serializing format. Any value containing \r\n (binary serializers such as
    igbinary/msgpack, or multi-line text) reliably crashes the request. This is
    the crash tracked in #1574 — an unauthenticated, repeatable DoS.
  • Cluster: chunks are routed to different nodes by slot, so the byte stream
    is split across sockets. The smuggled command arrives on a node whose stream
    is clean and is executed, though the application never sent it:
    • FLUSHDB wipes an entire shard. It has no key but is routable because
      ClusterStrategy::getFakeKey() hardcodes the fake key 'key'
      (https://github.com/predis/predis/blob/v3.2.0/src/Cluster/ClusterStrategy.php#L56
      and #L243-L246), so the smuggled command always lands on the node serving
      slot('key').
    • INFO (same fake-key routing) leaks server configuration via orphaned
      responses; CLUSTER FLUSHSLOTS can take a node down.
    • Same-slot GET/SET/DEL allow key theft (the reply is attributed to the
      application's own later command on that slot), cache poisoning and targeted
      data destruction; junk-key floods can exhaust node memory (OOM / mass
      eviction of legitimate keys).
    • Lua execution is not reachable: EVAL cannot be reconstructed (the
      class is EVAL_ due to the PHP reserved word), EVAL_RO fails the Keys
      trait validation, and EVALSHA requires a pre-loaded script. This is
      accidental, not a designed mitigation, and does not reduce severity —
      FLUSHDB/DEL/SET alone already permit full cache wipes and data
      destruction.

Affected versions. Introduced in v3.0.0 by PR #1438 ("Improved pipeline
abstractions"). Affected range: 3.0.0-RC1 through 3.2.0 (v3.0.0-alpha1 is not
affected — the vulnerable code was added after it). v1.x and v2.x are not
affected; their pipelines write per-command via writeRequest() and the
vulnerable code path does not exist.

Only pipeline() reaches the vulnerable sink; transaction() / MULTI paths do
not.

Proof of concept

Two plain redis:8 containers acting as two shards (PredisCluster shards
client-side, so Redis itself need not be in cluster mode); a PHP app on a
vulnerable Predis checkout (e.g. v3.2.0).

docker-compose.yml:

services:
  redis1:
    image: redis:8
    ports: ["6391:6379"]
  redis2:
    image: redis:8
    ports: ["6392:6379"]

index.php (a normal-looking app — slug from URL → cache lookup):

<?php
require __DIR__ . '/vendor/autoload.php';

$nodes = ['tcp://127.0.0.1:6391', 'tcp://127.0.0.1:6392'];
$client = new Predis\Client($nodes, ['cluster' => 'predis',
'parameters' => ['read_write_timeout' => 2]]);

if (isset($_GET['seed'])) {
for ($i = 1; $i <= 100; $i++) { $client->set("user:$i", "data$i"); }
exit('seeded');
}

$slug = $_GET['slug'] ?? '';
try {
[$doc] = $client->pipeline()->get("slug:$slug")->execute();
echo $doc ?: 'no such slug';
} catch (Throwable $e) {
http_response_code(500);
echo get_class($e);
}

Run:

composer require predis/predis:3.2.0
docker compose up -d
php -S 127.0.0.1:8080 -t .
curl 'http://127.0.0.1:8080/?seed'                       # 100 keys

Attack (smuggled FLUSHDB inside the slug):

curl 'http://127.0.0.1:8080/?slug=PAD4%0D%0A*1%0D%0A%247%0D%0AFLUSHDB'

The slug's first line must hash to a different shard than the fake key 'key'
(otherwise the truncated bytes swallow the injection and the request simply
404s). With two shards this is ~50% per attempt — retry PAD0, PAD1, … until
the request returns 500. More shards make the attack easier: the per-attempt
hit probability is (N-1)/N, so on production clusters with many shards the
first request succeeds with near-certainty.

Verified result: dbsize across both shards drops 100 → 62; one shard was wiped
by a FLUSHDB the application never issued (it only ever ran GET/SET on
normal keys). The fix was confirmed A/B: the same PoC wipes a shard on the parent
of commit 053cb4b6 and fails on 053cb4b6.

Impact

CWE-93 (Improper Neutralization of CRLF Sequences) leading to Redis command
injection / protocol smuggling and denial of service. Any application on
predis/predis 3.0.0-RC1 – 3.2.0 that calls pipeline() on a cluster or
replication connection and includes attacker-influenced data (values or
keys — e.g. cache keys built from URL slugs) in the pipelined commands is
affected. This is a common pattern for cache lookups, sessions and queued
writes.

  • Cluster: unauthenticated remote command injection — shard-wide cache wipe
    (FLUSHDB), targeted destruction (DEL), cache poisoning (SET), same-slot
    key theft (GET), node memory exhaustion (key flood), possible cluster outage
    (CLUSTER FLUSHSLOTS).
  • Replication: reliable unauthenticated DoS on every affected request.

Remediation

Upgrade to predis/predis 3.3.0 or later. The fix (PR #1586, commit
053cb4b6) makes pipelines on aggregate connections write each command using the
real Command object, eliminating the second, byte-splitting parser.

Users who cannot upgrade immediately should avoid calling pipeline() on
aggregate (cluster / replication) connections with any attacker-influenced keys
or values; there is no reliable in-application way to neutralize the embedded
\r\n while the second parser remains in the code path.

Release Notes

3.6.0

Added

  • Added support for new TS commands + Indonesian language support integration test (#1695)
  • Added support for new COLLECT reducer for FT.AGGREGATE (#1699)
  • Added support for SDIFFCARD and SUNIONCARD command
  • Added support for MAXCOUNT and MAXSIZE arguments for XREAD and XREADGROUP
  • Added support for TS.READ command
  • Added support for EXCLUDEEMPTY argument for TS.MRANGE and TS.MREVRANGE commands
  • Added explicit testing for FT.SEARCH timeout policies
  • Added support for TS.QUERYLABELS command
  • Added support for LMOVEM and BLMOVEM commands
  • Added support for FT.ALIASLIST command
  • Added stream commands to ClusterStrategy
  • Added vector sets commands to ClusterStrategy
  • Added experimental support for HIMPORT bulk hash import feature (API may change in a future release)

Changed

  • Added OBJECT and hash field expiration commands to ClusterStrategy
  • Make ZMSCORE command prefixable and add to ClusterStrategy (#1692)

Fixed

  • Fixed Sentinel does not wipe servers on exception caused (#1694)
  • Fixed @method cmsincrby() annotation

3.5.1

Added

  • Expose pipeline() API via ClientInterface (#1686)

Fixed

  • Allow UNLINK to accept an array of keys (#1687)

3.5.0

Added

  • Added support for XNACK command (#1666)
  • Added support for INCREX command (#1674)
  • Added support for UNLINK command (#1680)
  • Added support for AR* array commands (#1672)
  • Handle Redis Cluster -READONLY responses failover events (#1656)
  • Added FPHA argument for JSON.SET command (#1661)
  • Added new COUNT aggregator for Sorted Set commands (#1668)
  • Added support for multiple aggregators for TS.range commands (#1670)

Changed

  • Include command name in unsupported container command error messages (#1653)

Fixed

  • Fixed handling of gap slots in SlotMap::offsetUnset() (#1660)
  • Fixed ZRANGE to include 6.2 arguments (#1662)
  • Fixed Sentinel retry to narrow retryable exceptions to CommunicationException (#1665)
  • Fixed SENTINEL SLAVES RESP3 incompatible response (#1676)

3.4.2

Changed

  • Switch to static closures

Fixed

  • Fixed Sentinel getParameters() executed on string configuration (#1649)
  • Fixed Sentinel discovery methods not catching StreamInitException on connection failure (#1650)

3.4.1

Added

  • Made H(P)TTL commands prefixable (#1639)
  • Made (B)LMPOP commands prefixable (#1643)

Fixed

  • Fixed Sentinel getParameter() call on array error (#2423)
  • Removed deprecated static from callables (#1642)

3.4.0

This release introduces two major improvements: a refactored handshake process and built-in retry support.

The redesigned handshake session delivers up to 25% performance improvement compared to the previous version.

With the newly added retry support, you can automatically retry command execution on transient failures before giving up. This helps mitigate network glitches and delegate error handling to Predis.

For more details, see the documentation.

Added

  • Added optional retry support (#1616)
  • Added support for VRANGE command (#1623)
  • Added support for idempotent stream API (#1632)
  • Added support for HOTKEYS container command (#1630)

Fixed

  • Fixed [L|R]PUSHX variadic arguments normalization (#1633)
  • Fixed wrong @param annotation in Parameters (#1614)
  • Made ZRANDMEMBER prefixable (#1621)
  • Improve connection handshake by pipelining commands (#1622)

3.3.0

Added

  • Added cluster support for XADD, XDEL and XRANGE (#1587)
  • Added prefixable interface for HEXPIRE and HEXPIRETIME (#1592)
  • Added new experimental CAS/CAD functionality (#1609)
  • Added temporary XREADGROUP_CLAIM command (#1608)
  • Added support for MSET command (#1610)
  • Added experimental support for FT.HYBRID (#1607)

Changed

  • Improved compatiblity with Relay (#1597)
  • Refactor pipeline data writing depends on connection type (#1586)

3.2.0

Added

  • Added support for XDELEX and XACKDEL (#1580)
  • Added missing VSIM argument (#1582)

Changed

  • Extended XTRIM and XADD commands with new parameters (#1580)

Maintenance

  • Updated Redis 8.2 test image (#1583)
  • Added test coverage for updated Vamana (#1584)

3.1.0

Added

  • Add experimental support for vector sets commands (#1550)
  • Added support for XACK command (#1555)
  • Added support for XCLAIM command (#1557)
  • Added support for XPENDING command (#1558)
  • Added support for XSETID command (#1559)
  • Added validation and support for the new BITOP command operations (#1566)

Changed

  • Handle and retry LOADING errors from Sentinel replicas (#1536)
  • Retry all exceptions from Sentinel replicas (#1577)

Fixed

  • Fixed PHP 8.4 deprecated call to stream_context_set_option() (#1545)
  • Fixed return type for ZCOUNT to be int (#1547)
  • Fixed throwing CommunicationException when stream is EOF (#1548)
  • Removed automatic conn_uid parameter assignment (#1552)
  • Fixed wrong command API call on prefix processing (#1554)
  • Fixed XREAD response parsing while read null (#1563)
  • Fixed XINFO command responses parsing (#1560)
  • Marked missing commands as Prefixable (#1576)

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.


Depfu Status

Depfu will automatically keep this PR conflict-free, as long as you don't add any commits to this branch yourself. You can also trigger a rebase manually by commenting with @depfu rebase.

All Depfu comment commands
@​depfu rebase
Rebases against your default branch and redoes this update
@​depfu recreate
Recreates this PR, overwriting any edits that you've made to it
@​depfu merge
Merges this PR once your tests are passing and conflicts are resolved
@​depfu cancel merge
Cancels automatic merging of this PR
@​depfu close
Closes this PR and deletes the branch
@​depfu reopen
Restores the branch and reopens this PR (if it's closed)
@​depfu pause
Ignores all future updates for this dependency and closes this PR
@​depfu pause [minor|major]
Ignores all future minor/major updates for this dependency and closes this PR
@​depfu resume
Future versions of this dependency will create PRs again (leaves this PR as is)

@depfu depfu Bot added the depfu label Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants