VYPR
Low severityNVD Advisory· Published Feb 9, 2023· Updated Aug 2, 2024

CVE-2023-22795

CVE-2023-22795

Description

A regular expression based DoS vulnerability in Action Dispatch <6.1.7.1 and <7.0.4.1 related to the If-None-Match header. A specially crafted HTTP If-None-Match header can cause the regular expression engine to enter a state of catastrophic backtracking, when on a version of Ruby below 3.2.0. This can cause the process to use large amounts of CPU and memory, leading to a possible DoS vulnerability All users running an affected release should either upgrade or use one of the workarounds immediately.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

ReDoS vulnerability in Ruby on Rails Action Dispatch allows attacker-crafted If-None-Match headers to cause catastrophic backtracking, leading to denial of service.

Vulnerability

Overview

A regular expression denial-of-service (ReDoS) vulnerability exists in the Action Dispatch component of Ruby on Rails versions prior to 6.1.7.1 and 7.0.4.1. The vulnerability is triggered by a specially crafted If-None-Match HTTP header that causes the regular expression engine to enter catastrophic backtracking, resulting in excessive CPU and memory consumption. This issue is exploitable only on Ruby versions below 3.2.0, as Ruby 3.2.0 introduced changes to mitigate such ReDoS patterns [1][2].

Exploitation

The attack vector is remote and requires no authentication. An attacker sends an HTTP request with a malicious If-None-Match header to an affected Rails application. The server processes this header against a vulnerable regular expression, leading to exponential backtracking. No special privileges or user interaction is needed; the mere processing of the request by Action Dispatch triggers the vulnerability [1].

Impact

Successful exploitation causes the server process to consume large amounts of CPU and memory, significantly degrading performance and potentially leading to a complete denial of service (DoS) for legitimate users. Since the If-None-Match header is commonly processed in HTTP request handling, many Rails endpoints could be affected [1][3].

Mitigation

Users should upgrade to Rails 6.1.7.1, 7.0.4.1, or later where the vulnerability is patched. For those unable to upgrade, a workaround involves applying a monkey-patch to avoid the vulnerable regular expression, as detailed in the Rails advisory. The fix, visible in commit 8dc4595, modifies the regex to prevent catastrophic backtracking [2]. No CVSS score has been officially published as of the CVE date [1].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
actionpackRubyGems
>= 4.0.0.beta1, < 6.1.7.16.1.7.1
actionpackRubyGems
>= 7.0.0, < 7.0.4.17.0.4.1

Affected products

10

Patches

3
8dc45950619a

Avoid regex backtracking on If-None-Match header

https://github.com/rails/railsJohn HawthornJan 13, 2023via ghsa
1 file changed · +1 1
  • actionpack/lib/action_dispatch/http/cache.rb+1 1 modified
    @@ -18,7 +18,7 @@ def if_none_match
             end
     
             def if_none_match_etags
    -          if_none_match ? if_none_match.split(/\s*,\s*/) : []
    +          if_none_match ? if_none_match.split(",").each(&:strip!) : []
             end
     
             def not_modified?(modified_at)
    
8d82687f3b04

Avoid regex backtracking on If-None-Match header

https://github.com/rails/railsJohn HawthornJan 13, 2023via ghsa
1 file changed · +1 1
  • actionpack/lib/action_dispatch/http/cache.rb+1 1 modified
    @@ -18,7 +18,7 @@ def if_none_match
             end
     
             def if_none_match_etags
    -          if_none_match ? if_none_match.split(/\s*,\s*/) : []
    +          if_none_match ? if_none_match.split(",").each(&:strip!) : []
             end
     
             def not_modified?(modified_at)
    
cd461c3e64e0

Support for multiple etags in an If-None-Match header

https://github.com/rails/railsTravis WarlickAug 14, 2011via ghsa
3 files changed · +48 1
  • actionpack/CHANGELOG.md+2 0 modified
    @@ -1,5 +1,7 @@
     ## Rails 4.0.0 (unreleased) ##
     
    +*   Support multiple etags in If-None-Match header. *Travis Warlick*
    +
     *   Allow to configure how unverified request will be handled using `:with`
         option in `protect_from_forgery` method.
     
    
  • actionpack/lib/action_dispatch/http/cache.rb+7 1 modified
    @@ -17,12 +17,18 @@ def if_none_match
               env[HTTP_IF_NONE_MATCH]
             end
     
    +        def if_none_match_etags
    +          (if_none_match ? if_none_match.split(/\s*,\s*/) : []).collect do |etag|
    +            etag.gsub(/^\"|\"$/, "")
    +          end
    +        end
    +
             def not_modified?(modified_at)
               if_modified_since && modified_at && if_modified_since >= modified_at
             end
     
             def etag_matches?(etag)
    -          if_none_match && if_none_match == etag
    +          if_none_match_etags.include?(etag)
             end
     
             # Check response freshness (Last-Modified and ETag) against request
    
  • actionpack/test/dispatch/request_test.rb+39 0 modified
    @@ -746,6 +746,45 @@ def url_for(options = {})
         assert_equal "/foo?bar", path
       end
     
    +  test "if_none_match_etags none" do
    +    request = stub_request
    +
    +    assert_equal nil, request.if_none_match
    +    assert_equal [], request.if_none_match_etags
    +    assert !request.etag_matches?("foo")
    +    assert !request.etag_matches?(nil)
    +  end
    +
    +  test "if_none_match_etags single" do
    +    header = 'the-etag'
    +    request = stub_request('HTTP_IF_NONE_MATCH' => header)
    +
    +    assert_equal header, request.if_none_match
    +    assert_equal [header], request.if_none_match_etags
    +    assert request.etag_matches?("the-etag")
    +  end
    +
    +  test "if_none_match_etags quoted single" do
    +    header = '"the-etag"'
    +    request = stub_request('HTTP_IF_NONE_MATCH' => header)
    +
    +    assert_equal header, request.if_none_match
    +    assert_equal ['the-etag'], request.if_none_match_etags
    +    assert request.etag_matches?("the-etag")
    +  end
    +
    +  test "if_none_match_etags multiple" do
    +    header = 'etag1, etag2, "third etag", "etag4"'
    +    expected = ['etag1', 'etag2', 'third etag', 'etag4']
    +    request = stub_request('HTTP_IF_NONE_MATCH' => header)
    +
    +    assert_equal header, request.if_none_match
    +    assert_equal expected, request.if_none_match_etags
    +    expected.each do |etag|
    +      assert request.etag_matches?(etag), etag
    +    end
    +  end
    +
     protected
     
       def stub_request(env = {})
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

12

News mentions

0

No linked articles in our index yet.