I've seen a lot of people doing it and I used to do it too, use params.merge(…)
in link_to or redirect_to calls. You do this because you want to change one ore more parameters (like the locale, current page or search query) but leave the rest as it is.
But heads up! There is an issue with this solution:
link_to("Page 2", params.merge(page: 2)) # /users?page=2
link_to("parlez vous français?", params.merge(locale: "fr")) # /fr/about
What do you think would this request url do to the second link? Hint: You won't get a french customer anymore :)
http://localhost:3000/en/about?%68%6f%73%74=%77%77%77%2e%35%30%72%65%61%73%6f%6e%73%74%6f%68%61%74%65%74%68%65%66%72%65%6e%63%68%2e%63%6f%6d%2f%23&%70%6f%72%74=%38%30
equals
http://localhost:3000/en/about?host=www.50reasonstohatethefrench.com/%23&port=80
which results in you link target being
http://www.50reasonstohatethefrench.com/#/fr/about
There is also protocol
you could tamper with. So if you have french customers you might want to know how to get out of this misery.
Usually it is the best to explicitly define which parameters you want and strip the others away but in some cases it is really annoying or just impossible. You should be on the safe side if you pass only_path: true
to the url_for method. This will ensure that you just get the path instead of the full URL. The three parameters protocol, host and port are getting stripped away entirely.
link_to("parlez vous français?", params.merge(locale: "fr", only_path: true)) # /fr/about
This can get a bit cumbersome so if you need this multiple times you might want to add this to your application controller:
# safe url parameters to use instead of params.merge
def safe_params unsafe = {}
params.merge(unsafe).merge(only_path: true, script_name: nil)
end
helper_method :safe_params
This way you can save a bit on the link_to side:
link_to("parlez vous français?", safe_params(locale: "fr"))
And don't think you can solve this with default_url_params
as you can also pass this via the URL!
link_to("Homepage", @user.homepage)
Rails escapes the parameters good enough that you won't directly run into XSS issues here but there are exceptions in regard to javascript:
or data:
links. So if you link to a user defined webpage, be sure to blacklist or better whitelist and enforce the allowed protocols.
Update: script_name
There is another parameter which can alter the URL and is therefore dangerous to ignore. The popular pagination gem will_paginate ran into this problem as well. The safe_params
helper was modified accordingly.