Patching the PostgreSQL DoS Vulnerability (CVE-2022-44566) in Active Record
When you operate Ruby on Rails applications at scale, database performance is paramount. CVE-2022-44566 exposes a critical flaw in how Active Record’s PostgreSQL adapter handles extremely large numbers, allowing attackers to trigger severe database slowdowns.
There are several key takeaways to consider:
- The Vulnerability: CVE-2022-44566 is a Denial of Service (DoS) vulnerability with a “High” severity rating (CVSS 7.5)1.
- The Mechanism: Supplying an integer larger than a 64-bit signed representation forces the PostgreSQL adapter to cast the value as
numeric. This subtle type change causes PostgreSQL to bypass indexes and perform a slow sequential scan on the target table. - Affected Versions: Active Record versions
< 7.0.4.1and< 6.1.7.1are vulnerable.2 - The Solution: The most durable fix is to upgrade
activerecordto7.0.4.1,6.1.7.1, or a patched 5.2 version. - Immediate Mitigation: If upgrading is blocked, you must strictly validate user input to reject floats or integers exceeding 64-bit bounds before they reach Active Record clauses.
The Architecture of Database Retrieval
In 1890, the US Census Bureau faced a data processing crisis. The 1880 census had taken eight years to tabulate manually, and the 1890 census was projected to take over a decade. The solution came in the form of Herman Hollerith’s electromechanical punch card tabulator, which indexed and sorted data automatically. The tabulator allowed the operators to retrieve and aggregate data without scanning every single piece of paper sequentially.
Similarly, modern relational databases rely on B-tree indexes to look up records instantly. When indexes work correctly, querying a table with ten million rows takes milliseconds. When indexes fail, the database must fall back to checking every single row – a process known as a sequential scan.
This brings us to CVE-2022-44566. Strictly speaking, this vulnerability is an attack on the database’s ability to use its indexes, intentionally triggering slow sequential scans that exhaust system resources.
Understanding the PostgreSQL Adapter Vulnerability
To understand why this happens, we have to look at how Active Record translates Ruby objects into SQL.
Active Record, generally speaking, abstracts away the differences between Ruby’s flexible types and the database’s rigid schema. In Ruby, an Integer can grow infinitely large (what used to be called a Bignum in older Ruby versions). PostgreSQL, however, has strict limits: a standard integer maxes out at 2,147,483,647, and a bigint (64-bit signed integer) maxes out at 9,223,372,036,854,775,807.
Before the patches for CVE-2022-44566, if you passed a number larger than the 64-bit maximum to the PostgreSQL adapter, it gracefully converted the value to a PostgreSQL numeric type – a variable-precision type designed for arbitrary calculations.
This leads naturally to our next question: why is converting to a numeric type dangerous?
The danger lies in how the PostgreSQL query planner evaluates types. When PostgreSQL is asked to compare an integer or bigint column against a numeric value, it often determines that the existing B-tree indexes for that column cannot be safely used for the comparison. It discards the index and performs a full sequential scan.
A Practical Example
Let’s illustrate that with a somewhat contrived, but very realistic, scenario. Suppose you have a Web application that allows users to view an order via its ID. The controller might look like this:
def show
@order = Order.find_by(id: params[:id])
# ...snip...
end
If a regular user visits /orders/123, Active Record generates a safe, indexed query:
SELECT "orders".* FROM "orders" WHERE "orders"."id" = 123 LIMIT 1;
Now, suppose an attacker writes a script that requests /orders/9999999999999999999999999999.
Because that number is far larger than a 64-bit integer, the vulnerable PostgreSQL adapter treats it as numeric. PostgreSQL receives the query, ignores the primary key index on the id column, and begins a sequential scan of the entire orders table.
If your orders table has 50 million rows, this single request might spike the database CPU to 100% for several seconds. An attacker only needs to send a handful of these requests concurrently to completely saturate the database connections, resulting in a full Denial of Service for all your users.
Patching CVE-2022-44566
The most durable and responsible way to resolve this vulnerability is to upgrade the activerecord gem. The Rails core team released patches that prevent this unsafe type coercion.
Fixed Versions
The vulnerability is patched in the following Active Record series releases:
>= 7.0.4.1>= 6.1.7.12
(Note: There were also patches provided for the 5.2 series (~> 5.2.8), though standard support for 5.2 ended long ago. If your project is still on Rails 5.2, you likely have broader maintenance challenges ahead, but you should apply the security patch immediately.)
Upgrade Walkthrough
In order to dig a bit deeper into the mechanics of upgrading, let’s walk through the patch process.
First, let’s verify our current Active Record version:
$ bundle info activerecord
* activerecord (7.0.4)
Summary: Object-relational mapper framework (part of Rails).
Homepage: https://rubyonrails.org
Path: /path/to/gems/activerecord-7.0.4
You also may notice the Path output showing exactly where the gem is installed on your system. This is a helpful indicator to verify which exact directory your application will load the code from.
Before we run any commands that modify our application’s state, we should ensure our Gemfile and Gemfile.lock are committed to source control. This gives us a safe checkpoint to return to if the upgrade process introduces unexpected changes.
To safely patch the vulnerability without accidentally upgrading to a new major or minor version, we can use the --patch flag with the bundle update command:
$ bundle update --patch activerecord rails
Alternatively, you can manually adjust your Gemfile to require the specific patched version, like gem 'rails', '~> 7.0.4.1', and run bundle install.
After running the update command, verify the installation:
$ bundle info activerecord
* activerecord (7.0.4.1)
Summary: Object-relational mapper framework (part of Rails).
Homepage: https://rubyonrails.org
Path: /path/to/gems/activerecord-7.0.4.1
Of course, after the update succeeds, you must run your test suite to ensure the patch hasn’t introduced any unexpected behavior in your specific environment. Only after verifying the test suite should you commit the updated Gemfile.lock to source control.
Workarounds Without Upgrading
Of course, we don’t always have the luxury of upgrading dependencies immediately. Perhaps you are dealing with a legacy application bound to an older Ruby version, or your CI pipeline is currently blocked.
If you cannot immediately upgrade, you must implement strict input validation to prevent integers wider than a signed 64-bit representation (or floats) from reaching Active Record clauses.
You can accomplish this by sanitizing inputs before they hit your queries. For example, if you know an ID must always fit within standard constraints, you could validate it at the controller level:
class ApplicationController < ActionController::Base
# 64-bit signed integer maximum: 9,223,372,036,854,775,807
MAX_BIGINT = 9223372036854775807
def safe_id(value)
# First, ensure the value only contains digits
return nil unless value.to_s.match?(/\A\d+\z/)
# Then, ensure the value is within the 64-bit maximum bounds
int_value = value.to_i
if int_value <= MAX_BIGINT
int_value
else
nil
end
end
end
Then use it in your actions:
def show
valid_id = safe_id(params[:id])
@order = Order.find_by(id: valid_id)
head :not_found unless @order
end
A Word of Caution: While workarounds like this are helpful, they are inherently brittle. You must remember to apply this validation anywhere user input is passed directly to an Active Record query. It’s quite common for a developer to miss a spot – perhaps in a background job or an obscure administrative search filter.
Therefore, although this workaround is a reasonable temporary action, one must assume that relying solely on manual input validation for framework-level vulnerabilities will eventually lead to an oversight. The only truly sustainable solution is to upgrade the framework.
Conclusion
CVE-2022-44566 serves as an excellent reminder of the complexities hiding beneath our database abstractions. Type casting is a powerful feature that makes Ruby on Rails highly productive, but when the database planner interprets those types differently than we expect, the performance consequences can be severe.
By understanding the mechanics of this DoS vulnerability, prioritizing the framework upgrade, and thoughtfully applying input validation when necessary, you ensure your application remains both performant and resilient against attack.2
Footnotes
-
National Vulnerability Database, “NVD – CVE-2022-44566 Detail,” National Institute of Standards and Technology, last modified March 25, 2025, https://nvd.nist.gov/vuln/detail/CVE-2022-44566. ↩
-
John Hawthorn, “[CVE-2022-44566] Possible Denial of Service Vulnerability in ActiveRecord’s PostgreSQL Adapter,” Ruby on Rails Discussions, January 17, 2023, https://discuss.rubyonrails.org/t/cve-2022-44566-possible-denial-of-service-vulnerability-in-activerecords-postgresql-adapter/82119. ↩ ↩2 ↩3
You May Also Like
CVE-2008-5189: Ruby on Rails CRLF Injection
A detailed look at CVE-2008-5189, a CRLF injection vulnerability in early versions of Ruby on Rails that enabled HTTP Response Splitting via the redirect_to method.
CVE-2008-7248: Bypassing CSRF Protection with text/plain in Ruby on Rails
An analysis of CVE-2008-7248, a vulnerability in Ruby on Rails ActionPack that allowed attackers to bypass Cross-Site Request Forgery (CSRF) protection using the text/plain content type.
CVE-2008-7310: Spree Hash Restriction Weakness
An analysis of CVE-2008-7310, a mass assignment vulnerability in early versions of the Spree e-commerce framework for Ruby on Rails that allowed attackers to bypass the payment process.