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" W开发者_开发知识库HERE "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?
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:
- Have no security/login required, or
- Have already logged in and are making the requests from script hosted on the same domain, or
- Are making the requests via Fiddler or similar, (bypassing the browser's built-in protections).
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:
- First, if you get a PUT or DELETE, there is nowhere to put the
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. - Second, since HTML forms can only use POST and GET, if the request is a PUT or DELETE the attacker cannot use a HTML form to force or trick the user into submitting the request. They can use XMLHttpRequest, but XMLHttpRequest does not allow cross-site requests (unless enabled by security settings on both sites).
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:
- http://blogs.msdn.com/b/bryansul/archive/2008/08/15/rest-and-xsrf-part-one.aspx
精彩评论