Search code examples
ruby-on-railsruby-on-rails-3ruby-on-rails-4rails-routing

Rails 3 and beyond: default implicit routing rule is considered a bad practice, why?


Before new Rails routing DSL was introduced in Rails 3, most applications sported simple, yet effective default routing rule that fit 99% of scenarios and in fact made it possible to do tons of stuff without even thinking of touching routing config:

# Install the default route as the lowest priority.
map.connect ':controller/:action/:id.:format'
map.connect ':controller/:action/:id'

If I understand correctly, since introducing of new routing DSL in Rails 3, this practice is deprecated. I know that this behavior can be sort of emulated using dynamic segments definition, but anyway, there's no "default route of lowest priority" generated anymore.

Why is it so? Any rationale? Current documentation or RailsCasts explain syntax in detail, but they really don't give any information on why it is considered a bad, outdated practice to use catch-all lowest priority default routing rule?


Solution

  • You should not use map.connect in Rails 2 just as you should not use dynamic segments and match in Rails 3. Catch-all routing is vulnerable to CSRF attacks. You'll probably learn a lot more from researching this than me trying to explaining it to you but basically:

    1. POST requests are the only types of requests that should be able to change the state of an application. So you usually change state through HTML form submissions.

    2. Using catch-all routing, both GET and POST requests get redirected to the same actions. This means that you can change state using GET requests!

    3. This is incredibly dangerous. Let's imagine that some bank uses catch-all routing. I can forge a url like this:

      http://somebank.com/withdraw?amount=1000000&from=GreyCat&to=Ashitaka
      

      And send you the link. By accessing it, you would change the application state without submitting an HTML form through a POST request.

    This is why, in Rails 4, match was fixed to only work with the via option, like so:

    match "/users/:id" => "users#show", :via => :get
    

    People were using match willy-nilly without taking into consideration the problems they were creating by using it. So now match needs to be written like that. However, you could (and should) rewrite it as:

    get "/users/:id" => "users#show"
    

    I always like to link to our Russian Rails hacker security researcher Egor Homakov's blog because he explains the security problems he finds with lots of passion and fervour. And he actually contributes to Rails and tries to improve its security so he deserves all the recognition he can get. Here's his blog post explaining why match is evil.