Rails published ten security advisories on 4 November. One is rated High, and it is a six-year-old loose end.

GHSA-wm3x-v3qw-4jh3 describes a Strong Parameters bypass in Action Pack. Strong Parameters is the mechanism that stops a form submission from setting any model attribute it likes. It is the single piece of Rails standing between a web form and an authorisation flag.

What the bug is

ActionController::Parameters#each_key delegates to the internal hash that holds the request parameters. When it is called with a block, it returns that hash rather than the Parameters instance.

The hash does not respond to the method that marks parameters as permitted. So when it reaches Active Model mass assignment, through new or assign_attributes or update, nothing raises the forbidden attributes error. The assignment simply happens.

An application that passes the return value of each_key into mass assignment therefore lets a remote, unauthenticated attacker set attributes it never permitted. The advisory names the obvious one: an authorisation flag.

Severity High, CVSS 7.5, integrity only, and no CVE assigned even now.

The six-year part

The advisory says, in its own words, that this is an incomplete fix of CVE-2020-8164.

That 2020 advisory addressed the same class of problem by making each, each_pair and each_value return the Parameters instance instead of the underlying hash. There are four such methods. Three were fixed. each_key was left delegating.

Six years. The fix was understood, the pattern was written down in a published advisory, and one method did not get it.

This is worth sitting with, because it is not a story about Rails being careless. An incomplete fix is the most ordinary failure in security engineering: the reported case gets fixed and the variants nobody demonstrated do not. The 2020 report presumably arrived with a proof of concept using each. Nobody wrote the one using each_key, so nobody looked for it.

Variant analysis is the name for doing that work deliberately. It is also, as it happens, one of the things the automated pipelines are now being pointed at, and this is exactly the shape of bug they are good at.

Who is affected

Narrower than it sounds. Rails itself never makes the vulnerable call. An application is affected only if it passes the return value of each_key, called with a block, into mass assignment.

That is an unusual thing to write deliberately, and most applications will not have it. But it is the kind of line that turns up in a helper written years ago to normalise parameter keys, and it does not look wrong in review.

Affected: actionpack from 8.1 up to 8.1.4.1, and anything below 8.0.5.2. Fixed in 8.1.4.1 and 8.0.5.2. The workaround is to call each_key for its block only and pass the permitted Parameters instance to mass assignment. Patches are published for both branches for anyone who cannot upgrade immediately.

It was reported responsibly by a HackerOne researcher credited as smlee.

The other nine

The same release covers a possible restoration of destroyed sessions in Action Dispatch's cache store, denial of service in Active Storage filename handling, cache poisoning in Action Pack content negotiation, denial of service in Action Controller live streaming and in Active Storage proxy mode, cross-site scripting in the Action View highlight helper and in the Active Support MessagePack serializer, a sensitive session information leak in Active Storage proxy mode, and a missing SameSite attribute on session cookies.

Nine moderate or low issues and one High. Strong Parameters leads because it is the one the framework tells you to rely on.

What to do

  • Upgrade to 8.1.4.1 or 8.0.5.2. The whole batch ships together.
  • Search your application for each_key called on a Parameters object, and for anything that treats the result of a Parameters iteration as a hash.
  • While you are in there, look for each_value and each_pair used the same way. Those were corrected in 2020, so they are safe to call, but the pattern is the thing worth finding.
  • Do not assume an advisory from 2020 closed the class of bug it described. This one did not.

What is not established

  • Whether it has been exploited. Nothing in the advisory suggests it has.
  • How many applications use the vulnerable pattern. There is no estimate and nobody is positioned to make one.
  • Why each_key was missed in 2020. The advisory states the fact and offers no account of it.
  • Whether a CVE will be assigned. The advisory says there is no known CVE.
  • Whether the versions reported elsewhere this week as fixing an arbitrary file write through ImageMagick uploads belong to this release. Several outlets paired these version numbers with that description, and we could not find a Rails advisory matching it.