Rails' url_for
is useful for generating routes from a Hash, but can lead to an
open redirect vulnerability
Show archive.org snapshot
.
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)
if expected_path != request.original_fullpath
redirect_to expected_path
end
This works, but doing it like that you've just introduced 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
.
request.path_parameters
. That will exclude values like :host
, :only_path
, etc and is safe to generate paths inside your application. Note that it does not include query parameters.redirect_to(url, allow_other_host: false)
to avoid redirecting to foreign hosts. Careful: this only works for redirect_to
. If you say e.g. link_to expected_path
you'll still generate a link to a foreign host.slice
and/or exclude
, like so: url_for(params.to_unsafe_h.exclude(:host, :protocol))
. To feel extra safe, you can add merge(only_path: true)
but excluding :host
should have taken care of that.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.