September 6, 2023·4 min read·engineeringvalkeyredissecurityacl

Redis repairs SORT_RO key discovery for ACL checks

Redis 7.2.1 fixes a missing key count that could bypass key-pattern authorization for SORT_RO. The patch makes the command's discovered-key result agree with its populated entries.

Access control can fail even when the permission checker itself follows its input correctly. It needs an accurate description of the resources a command will touch. Redis's September security fix repairs that description for SORT_RO, whose key-discovery helper populated a key entry without setting the result's key count.

The 7.2 release notes date Redis 7.2.1 to September 6 and identify CVE-2023-41053. The project advisory explains that incorrect key identification could grant access to keys not explicitly authorized by the user's ACL configuration. It names Redis 7.0 or newer as affected and identifies 7.0.13 and 7.2.1 as patched versions.

I have used Redis since its early releases, and I appreciate being able to express key permissions directly in the server. That policy is useful only if every command supplies the resources its checker needs to inspect. A command being read-only does not remove the confidentiality requirement for the keys it reads.

The missing count changed the result

The fixing commit shows a small implementation discrepancy. sortROGetKeys() allocated room for one key and set that entry's argument position to one. It also marked the key as read-only access. The function then returned one without updating result->numkeys.

The patch sets result->numkeys = 1 and returns that stored count. Both the result object and return value now describe the populated entry. The correction matters because callers can consume the result object, not merely the function's integer return.

The commit demonstrates the externally visible difference with COMMAND GETKEYS. Before the correction, asking for the keys used by a simple SORT_RO returned an empty array. Afterward, it returned the supplied key. The added regression test asserts that the command exposes that key through the discovery interface.

An illustrative inspection request has the following shape. It asks Redis to identify command keys, rather than requesting the sorted contents of the example key.

COMMAND GETKEYS SORT_RO example:list

The corrected result includes example:list. That output is important because the key is present even in the simplest form of the sorting command. The discovery function's own comment says the sort key is always present, which makes a zero-key result inconsistent with its intended contract.

Authorization iterates the stored count

The 7.2.1 ACL implementation shows why the result object matters. When a selector does not grant access to every key, the checker obtains the command's keys and loops over result->numkeys. For each entry it checks the argument at the recorded position against the selector's key permissions.

A populated array entry outside the reported count does not participate in that loop. The checker has no reason to inspect memory beyond the result's declared entries. The defect therefore lies in the resource description passed into authorization, not in a requirement that the authorization loop guess how much of the array is meaningful.

This pattern is easy to overlook in APIs that communicate through both a return value and an output structure. Updating one can make a helper look correct locally while leaving downstream consumers with another value. The patch aligns them at the point where the key-discovery result is constructed.

The regression test checks the discovery contract directly. It does not need a large dataset or a complicated sort expression to reveal the mismatch. The simple command has a mandatory source key, so the expected resource list is unambiguous.

Read-only still means access to data

The command definition describes SORT_RO as returning sorted elements of a list, set, or sorted set. It carries the read-only command flag. Its key metadata still marks the source as read access and associates the custom discovery function with the command.

Its metadata also places the command in the dangerous ACL category alongside its collection-type categories. Those classifications describe command selection, while the key access flags describe the resources in a particular invocation. Allowing a command and allowing its target key are separate decisions, so correct category metadata cannot compensate for an empty discovered-key result.

Read-only describes the operation's effect on stored state. Authorization separately decides whether the caller may inspect that state. A user may be permitted to sort an application's keys without being permitted to read every other namespace in a shared server.

The advisory's reported issue concerns that distinction. It does not claim that the command gained arbitrary write access or executed native code. The relevant failure is access outside the explicitly authorized key set. Keeping the impact precise helps explain why a read command belongs in a security release.

The command definition also marks optional BY and GET key relationships as unknown in static metadata because they can derive from stored contents. That is broader command context, but the patch at issue repairs the mandatory source key's count. It should not be described as a redesign that enumerates every possible indirect key in advance.

Metadata participates in enforcement

The corrected discovery helper makes the invariant explicit. It prepares one result, records the key position and access flags, sets the number of keys, and returns that same number. The authorization code can then apply its ordinary loop to a complete description.

This has implications for any database interface whose permissions depend on extracted command arguments. The resource extractor is part of enforcement. Testing only whether a command produces the right data leaves a gap when policy checks consume a separate description of its inputs.

I prefer the patch's direct invariant to a broad statement that permissions are stronger. The command always has a source key, and its discovery result now says so consistently. That restores the connection between the operation a user requests and the key-level permission the server needs to evaluate before allowing it.