Rails' url_for
is
useful for generating routes from a Hash
Show archive.org snapshot
, but can lead to an
open redirect vulnerability
Show archive.org snapshot
.
Your application's
generated route methods with a _url
suffix
Show archive.org snapshot
are also affected because
they use url_for
unter the hood
Show archive.org snapshot
.
The problem
Imagine your application contains code that checks if the current request's path is what it would generate internally.
If different, it would redirect users to the generated/expected path.
expected_path = url_for(params.to_unsafe_h) # ❌ this is not safe!
if expected_path != request.original_fullpath
redirect_to expected_path
end
While this works in terms of fixing the expected path, it introduces an Open Redirect vulnerability.
It's as simple as passing a host=evil.tld
URL parameter. Rails would see url_for(..., host: "evil.tld")
and happily generate a URL to that foreign host.
This makes phishing much easier, because an attacker can use links with your application's hostname, cause a redirect to evil.tld
and present a sign-in form which looks like yours. Users will enter their login credentials and submit them to evil.tld
.
What can I do?
- Use
redirect_to(url, allow_other_host: false)
to avoid redirecting to foreign hosts.
Careful: this only works forredirect_to
. For example,link_to(url)
will still generate a link to a foreign host and has no:allow_other_host
option. - If you want to redirect and keep all parameters, we strongly recommend redirecting from your routes.
- When you need to use
url_for
, userequest.path_parameters
andrequest.query_parameters
as described below.
If possible, specify allowed parameters explicitly, e.g.url_for(params.permit(:foo, :bar))
.
Rails 7.1+
url_for(path_params: request.path_parameters, params: request.query_parameters)
Rails versions before 7.1
url_for(**request.path_parameters, params: request.query_parameters)
Edge case for this approach: When a route contains path parameters like :host
(e.g. /foo/:host
), those will be part of request.path_parameters
and url_for(**request.path_parameters)
would then again generate a URL to a foreign host.
If your application defines such routes, you must mitigate the issue explicitly, or upgrade to Rails 7.1 and use its new :path_params
option.
We suggest you also add the following snippet which will raise a reminder message once you've upgraded to a newer Rails version.
if Gem::Version.new(Rails.version) >= Gem::Version.new("7.1")
raise "Replace **request.path_parameters with path_params: request.path_parameters"
end
Side note: Rails 7 mitigates Open Redirects by default
In Rails 7 applications, redirect_to
defaults to allow_other_host: false
.
If you want to redirect to foreign hosts, you must specify allow_other_host: true
instead.
As mentioned above, this helps only with redirect_to
, not if you place such URLs in links or similar.