Reputation: 2999
I made a demo application with rails new demo
and then generated a scaffolded user controller with rails generate scaffold User name:string email:string
. The scaffolded code has an ApplicationController
with protect_from_forgery
, so does UserController
which derives from ApplicationController
.
I run webrick, add a user, cool. Authenticity token works as promised with the POST on /users.
Yet still with Rails 3.0.5 I am able to do a:
niedakh@twettek-laptop:~$ telnet 10.0.0.4 3000
PUT /users/3 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 39
user[name]=vvvvv&user[email]=shiaus.pl
And have the user 3 modified without giving a token:
Started PUT "/users/3" for 10.0.0.4 at 2011-04-02 14:51:24 +0200
Processing by UsersController#update as HTML
Parameters: {"user"=>{"name"=>"vvvvv", "email"=>"shiaus.pl\r"}, "id"=>"3"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
', "updated_at" = '2011-04-02 12:51:24.437267' WHERE "users"."id" = 3s.pl
Redirected to http://10.0.0.4:3000/users/3
Completed 302 Found in 92ms
Also I can do the same with DELETE:
DELETE /users/3 HTTP/1.1
Which gives me:
Started DELETE "/users/3" for 10.0.0.4 at 2011-04-02 15:43:30 +0200
Processing by UsersController#destroy as HTML
Parameters: {"id"=>"3"}
SQL (0.7ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
AREL (0.5ms) DELETE FROM "users" WHERE "users"."id" = 3
Redirected to http://10.0.0.4:3000/users
Completed 302 Found in 180ms
Could you explain to me why can I do those things when I never send any token alongside those requests?
Upvotes: 1
Views: 1600
Reputation: 35613
Very Short Version: protect_from_forgery
is designed to protect against XSRF attacks from forged HTML FORM elements. PUT and DELETE are not vulnerable to XSRF attacks because HTML forms cannot use PUT or DELETE.
An XSRF (cross site request forgery) attack is where the victim browser is tricked into submitting a forged request to the server without interaction from the user.
Longer version: The reason you are able to do this is you either:
These are not the scenario protect_from_forgery
is designed to protect against.
The purpose of the protect_from_forgery
is to protect against XSRF attacks - Cross Site Request Forgery. This occurs when a user visiting an evil website (or a good website with added evil) is tricked into submitting a request to another website. For example you can trick a visitor into making any GET request, like this:
<img src="http://victim.com/victimPage?action=delete&id=ID12345" />
As soon as the victim visits the Evil site, his browser will automatically attempt to retrieve the image. This will obviously not retrieve an image, but meanwhile victim.com will execute the request deleting item ID12345. POST can be forged in a similar way, just create a form, and submit it to the foreign site using script, or else trick the user into clicking on it to submit.
That is where protect_from_forgery
comes in: The server sends the token to the client in a hidden field with the form. If no valid token appears, the server concludes that the form which was submitted isn't a submission of a genuine form sent by the server, so the request is rejected as potentially forged.
But you knew that.
The point is that HTTP forms can only use methods GET and POST, not PUT or DELETE. This has two effects:
protect_from_forgery
token. PUT or DELETE are not the result of a form submitting, so there is no way for the server to send the token to the client, therefore the client has no token to send back.This means that, provided the domain you host it on does not contain evil code itself, it is not necessary to protect PUT and DELETE from forgery. If the server does contain evil code, the attacker can make arbitrary XMLHttpRequest requests to get a valid token, and therefore easily circumvent the forgery protection anyway.
For a quick description of XSRF try here:
Upvotes: 11