Skip to content

Commit 7e69f65

Browse files
committed
Improper handling of proxy headers in Rack::Sendfile may allow proxy bypass.
- Ignore `HTTP_X_SENDFILE_TYPE` header from requests to prevent attackers from enabling sendfile features. - Only read `HTTP_X_ACCEL_MAPPING` when `x-accel-redirect` is explicitly configured and no app-level mappings exist. - Prefer `\A` instead of `^` to match the start of path mappings.
1 parent db6bc0f commit 7e69f65

File tree

3 files changed

+200
-50
lines changed

3 files changed

+200
-50
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/).
44

5+
## [3.1.18] - 2025-10-10
6+
7+
### Security
8+
9+
- [CVE-2025-61780](https://github.com/advisories/GHSA-r657-rxjc-j557) Improper handling of headers in `Rack::Sendfile` may allow proxy bypass.
10+
511
## [3.1.17] - 2025-10-07
612

713
### Security
@@ -373,6 +379,13 @@ Rack v3.1 is primarily a maintenance release that removes features deprecated in
373379
- Fix multipart filename generation for filenames that contain spaces. Encode spaces as "%20" instead of "+" which will be decoded properly by the multipart parser. ([#1736](https://github.com/rack/rack/pull/1645), [@muirdm](https://github.com/muirdm))
374380
- `Rack::Request#scheme` returns `ws` or `wss` when one of the `X-Forwarded-Scheme` / `X-Forwarded-Proto` headers is set to `ws` or `wss`, respectively. ([#1730](https://github.com/rack/rack/issues/1730), [@erwanst](https://github.com/erwanst))
375381

382+
## [2.2.20] - 2025-10-10
383+
384+
### Security
385+
386+
- [CVE-2025-61780](https://github.com/advisories/GHSA-r657-rxjc-j557) Improper handling of headers in `Rack::Sendfile` may allow proxy bypass.
387+
- [CVE-2025-61919](https://github.com/advisories/GHSA-6xw4-3v39-52mm) Unbounded read in `Rack::Request` form parsing can lead to memory exhaustion.
388+
376389
## [2.2.19] - 2025-10-07
377390

378391
### Security

lib/rack/sendfile.rb

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@ module Rack
1616
# delivery code.
1717
#
1818
# In order to take advantage of this middleware, the response body must
19-
# respond to +to_path+ and the request must include an x-sendfile-type
19+
# respond to +to_path+ and the request must include an `x-sendfile-type`
2020
# header. Rack::Files and other components implement +to_path+ so there's
21-
# rarely anything you need to do in your application. The x-sendfile-type
21+
# rarely anything you need to do in your application. The `x-sendfile-type`
2222
# header is typically set in your web servers configuration. The following
2323
# sections attempt to document
2424
#
2525
# === Nginx
2626
#
27-
# Nginx supports the x-accel-redirect header. This is similar to x-sendfile
27+
# Nginx supports the `x-accel-redirect` header. This is similar to `x-sendfile`
2828
# but requires parts of the filesystem to be mapped into a private URL
2929
# hierarchy.
3030
#
3131
# The following example shows the Nginx configuration required to create
32-
# a private "/files/" area, enable x-accel-redirect, and pass the special
33-
# x-sendfile-type and x-accel-mapping headers to the backend:
32+
# a private "/files/" area, enable `x-accel-redirect`, and pass the special
33+
# `x-accel-mapping` header to the backend:
3434
#
3535
# location ~ /files/(.*) {
3636
# internal;
@@ -44,24 +44,29 @@ module Rack
4444
# proxy_set_header X-Real-IP $remote_addr;
4545
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
4646
#
47-
# proxy_set_header x-sendfile-type x-accel-redirect;
4847
# proxy_set_header x-accel-mapping /var/www/=/files/;
4948
#
5049
# proxy_pass http://127.0.0.1:8080/;
5150
# }
5251
#
53-
# Note that the x-sendfile-type header must be set exactly as shown above.
54-
# The x-accel-mapping header should specify the location on the file system,
52+
# The `x-accel-mapping` header should specify the location on the file system,
5553
# followed by an equals sign (=), followed name of the private URL pattern
5654
# that it maps to. The middleware performs a simple substitution on the
5755
# resulting path.
5856
#
57+
# To enable `x-accel-redirect`, you must configure the middleware explicitly:
58+
#
59+
# use Rack::Sendfile, "x-accel-redirect"
60+
#
61+
# For security reasons, the `x-sendfile-type` header from requests is ignored.
62+
# The sendfile variation must be set via the middleware constructor.
63+
#
5964
# See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
6065
#
6166
# === lighttpd
6267
#
63-
# Lighttpd has supported some variation of the x-sendfile header for some
64-
# time, although only recent version support x-sendfile in a reverse proxy
68+
# Lighttpd has supported some variation of the `x-sendfile` header for some
69+
# time, although only recent version support `x-sendfile` in a reverse proxy
6570
# configuration.
6671
#
6772
# $HTTP["host"] == "example.com" {
@@ -83,7 +88,7 @@ module Rack
8388
#
8489
# === Apache
8590
#
86-
# x-sendfile is supported under Apache 2.x using a separate module:
91+
# `x-sendfile` is supported under Apache 2.x using a separate module:
8792
#
8893
# https://tn123.org/mod_xsendfile/
8994
#
@@ -97,16 +102,28 @@ module Rack
97102
# === Mapping parameter
98103
#
99104
# The third parameter allows for an overriding extension of the
100-
# x-accel-mapping header. Mappings should be provided in tuples of internal to
105+
# `x-accel-mapping` header. Mappings should be provided in tuples of internal to
101106
# external. The internal values may contain regular expression syntax, they
102107
# will be matched with case indifference.
108+
#
109+
# When `x-accel-redirect` is explicitly enabled via the variation parameter,
110+
# and no application-level mappings are provided, the middleware will read
111+
# the `x-accel-mapping` header from the proxy. This allows nginx to control
112+
# the path mapping without requiring application-level configuration.
113+
#
114+
# === Security
115+
#
116+
# For security reasons, the `x-sendfile-type` header from HTTP requests is
117+
# ignored. The sendfile variation must be explicitly configured via the
118+
# middleware constructor to prevent information disclosure vulnerabilities
119+
# where attackers could bypass proxy restrictions.
103120

104121
class Sendfile
105122
def initialize(app, variation = nil, mappings = [])
106123
@app = app
107124
@variation = variation
108125
@mappings = mappings.map do |internal, external|
109-
[/^#{internal}/i, external]
126+
[/\A#{internal}/i, external]
110127
end
111128
end
112129

@@ -145,22 +162,35 @@ def call(env)
145162
end
146163

147164
private
165+
148166
def variation(env)
149-
@variation ||
150-
env['sendfile.type'] ||
151-
env['HTTP_X_SENDFILE_TYPE']
167+
# Note: HTTP_X_SENDFILE_TYPE is intentionally NOT read for security reasons.
168+
# Attackers could use this header to enable x-accel-redirect and bypass proxy restrictions.
169+
@variation || env['sendfile.type']
170+
end
171+
172+
def x_accel_mapping(env)
173+
# Only allow header when:
174+
# 1. `x-accel-redirect` is explicitly enabled via constructor.
175+
# 2. No application-level mappings are configured.
176+
return nil unless @variation =~ /x-accel-redirect/i
177+
return nil if @mappings.any?
178+
179+
env['HTTP_X_ACCEL_MAPPING']
152180
end
153181

154182
def map_accel_path(env, path)
155183
if mapping = @mappings.find { |internal, _| internal =~ path }
156-
path.sub(*mapping)
157-
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
184+
return path.sub(*mapping)
185+
elsif mapping = x_accel_mapping(env)
186+
# Safe to use header: explicit config + no app mappings:
158187
mapping.split(',').map(&:strip).each do |m|
159188
internal, external = m.split('=', 2).map(&:strip)
160-
new_path = path.sub(/^#{internal}/i, external)
189+
new_path = path.sub(/\A#{internal}/i, external)
161190
return new_path unless path == new_path
162191
end
163-
path
192+
193+
return path
164194
end
165195
end
166196
end

0 commit comments

Comments
 (0)