I am working on our (newly renamed) authentication feature. The current scenario is: Scenario: Non-administrators should not set administrator ability Given I have no users And I add a user named "admin" as an administrator And I add a user named "myuser" as not an administrator When the user named "myuser" authenticates And the user enables the administrator role Then the user named "myuser" should not be an administrator Now, what I am looking for is an example of how an authenticated user would craft a post request in their browser to set the user.administrator flag to true. Crafting these sorts of http requests may be obvious and simple to some of you, but I have no clue how this is done. On some lists, asking questions on how to breach security are themselves a breach of list etiquette. If this is the case here then I ask your indulgence and the favour of a private reply if that is deemed more suitable. I do require the information though, since I have to defend against it. -- Posted via http://www.ruby-forum.com/.
On Fri, Jan 16, 2009 at 10:00 AM, James Byrne <lists at ruby-forum.com> wrote:> I am working on our (newly renamed) authentication feature. The current > scenario is: > > Scenario: Non-administrators should not set administrator ability > Given I have no users > And I add a user named "admin" as an administrator > And I add a user named "myuser" as not an administrator > When the user named "myuser" authenticates > And the user enables the administrator role > Then the user named "myuser" should not be an administrator > > Now, what I am looking for is an example of how an authenticated user > would craft a post request in their browser to set the > user.administrator flag to true. > > Crafting these sorts of http requests may be obvious and simple to some > of you, but I have no clue how this is done.Well, do you have a "set administrator" button? Use webrat to click it if you do. I assume you don''t though, cause that''d be kinda weird. How about passing it in the POST params: put users_url(user), :user => {:administrator => true} Something along those lines...> On some lists, asking questions on how to breach security are themselves > a breach of list etiquette. If this is the case here then I ask your > indulgence and the favour of a private reply if that is deemed more > suitable. I do require the information though, since I have to defend > against it.Asking how to test a security feature that you''re building is very different from asking how to hack somebody''s site :) Pat
James Byrne wrote:> > Now, what I am looking for is an example of how an authenticated user > would craft a post request in their browser to set the > user.administrator flag to true. >OK, I figured out how to do this, as an authenticated user, from the browser. I am now going to try the syntax as a string in visits and see if that triggers the problem. -- Posted via http://www.ruby-forum.com/.
Pat Maddox wrote:> I assume you don''t though, cause that''d be kinda weird. How about > passing it in the POST params: > > put users_url(user), :user => {:administrator => true} > > Something along those lines...That is the problem, I am not sure what syntax to use int the step definition. I tried this: visits "#{edit_user_path}?user[administrator]=1" Which produces the same type of url that the RoR security guide uses in its examples: http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1 Whereas I generate HTTP headers {"HTTP_REFERER"=>"http://www.example.com/account/edit?user[administrator]=1"} But this URL attack does not seem to work as advertised. The key "administrator" does not make it into the params hash: 200 OK [http://www.example.com/account/edit?user[administrator]=1] REQUESTING PAGE: POST /account with { "user"=>{ "name_middle"=>"Middle-myuser", "password_confirmation"=>"", "username"=>"myuser", "password"=>"", "email"=>"myuser at example.com", "name_first"=>"First-myuser", "name_last"=>"Last-myuser"}, "commit"=>"Update", "_method"=>"put"} I realize this is a silly thing to ask, but how do you do this for testing? -- Posted via http://www.ruby-forum.com/.
In the past we''ve done the following: Story: Users without hierarchy manager role accessing the hierarchy In order to ensure users that shouldn''t have access to the hierarchy don''t As a user who isn''t a hierarchy manager I should not be able to access the hierarchy Scenario: Non hierarchy manager attempting to access locations Given I''ve log in as a user without the ''hierarchy manager'' role When I try to GET /locations Then I am notified that I do not have access to that More Examples: | role | request_method | path | | hierarchy manager | POST | /locations | | hierarchy manager | PUT | /locations/1 | | hierarchy manager | DELETE | /locations/1 | etc .... The "Then" step ensures that the user is redirected to an access denied page. Granted, this doesn''t go the granularity you may be trying to get at, but knowing you aren''t actually getting through to the underlying action (by being redirected to the access denied page) has worked well for me, Zach On Fri, Jan 16, 2009 at 3:25 PM, James Byrne <lists at ruby-forum.com> wrote:> Pat Maddox wrote: > >> I assume you don''t though, cause that''d be kinda weird. How about >> passing it in the POST params: >> >> put users_url(user), :user => {:administrator => true} >> >> Something along those lines... > > That is the problem, I am not sure what syntax to use int the step > definition. I tried this: > > visits "#{edit_user_path}?user[administrator]=1" > > Which produces the same type of url that the RoR security guide uses in > its examples: > > http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1 > > Whereas I generate > > HTTP headers > {"HTTP_REFERER"=>"http://www.example.com/account/edit?user[administrator]=1"} > > But this URL attack does not seem to work as advertised. The key > "administrator" does not make it into the params hash: > > 200 OK [http://www.example.com/account/edit?user[administrator]=1] > REQUESTING PAGE: POST /account with { > "user"=>{ > "name_middle"=>"Middle-myuser", > "password_confirmation"=>"", > "username"=>"myuser", > "password"=>"", > "email"=>"myuser at example.com", > "name_first"=>"First-myuser", > "name_last"=>"Last-myuser"}, > "commit"=>"Update", > "_method"=>"put"} > > I realize this is a silly thing to ask, but how do you do this for > testing? > > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- Zach Dennis http://www.continuousthinking.com http://www.mutuallyhuman.com
Zach Dennis wrote:> > > The "Then" step ensures that the user is redirected to an access > denied page. Granted, this doesn''t go the granularity you may be > trying to get at, but knowing you aren''t actually getting through to > the underlying action (by being redirected to the access denied page) > has worked well for me,I am already testing for that. What I am trying to accomplish now is to find a malevolently crafted URL that will trigger the users_controller/update action with arbitrary contents in the params hash. Once I have one that "works" then we will code the model/controller to prevent it. I have gotten to the point where I believe that the url has to look somewhat like this: http://www.example.com/users/2/<some_action>?user[administrator=1]&commit=Update&action=update&_method=put&controller=users Where some_action is one of account, edit, update or nothing. I cannot yet determine which is the case. Regardless, I cannot seem to find a way to push this to the controller as a POST, which is apparently what the controller needs, from either a step definition or a browser. -- Posted via http://www.ruby-forum.com/.
On Fri, Jan 16, 2009 at 12:25 PM, James Byrne <lists at ruby-forum.com> wrote:> Pat Maddox wrote: > >> I assume you don''t though, cause that''d be kinda weird. How about >> passing it in the POST params: >> >> put users_url(user), :user => {:administrator => true} >> >> Something along those lines... > > That is the problem, I am not sure what syntax to use int the step > definition.Does put users_url(user), :user => {:administrator => true} not work? Also, I would probably be more likely to test this in a controller spec (where I know for sure the line I pasted will work), but I totally understand the desire to put it in an acceptance test. Pat
Pat Maddox wrote:> Does > put users_url(user), :user => {:administrator => true} > not work? >I have spent a good deal of time trying to get this to work with no success. I have read the api regarding the put method in: ActionController::Integration::Session which is what I believe provides the put method in the example, but I cannot seem to get the signature to work out. put(path, parameters = nil, headers = nil) If the path argument is "users_url(user)" then what is user? The Rails documentation describes the *_url methods thus:> Behind the scenes the *_url method wraps a call to url_for on the route hash. > > The *_url method takes a single optional argument which is a hash that > is merged on top of the url_for?ed route. This makes it so that a given > named route can be parametrized when used with redirect_to, link_to, etc.If I read this aright then in your example user should be a hash, but a hash of what? Where would I create it in my test and what should it contain? -- Posted via http://www.ruby-forum.com/.
James Byrne wrote:> > I have spent a good deal of time trying to get this to work with no > success. I have read the api regarding the put method in: > > ActionController::Integration::Session > > If I read this aright then in your example user should be a hash, but a > hash of what? Where would I create it in my test and what should it > contain?I ended up with this: :user => User.find(:first) When /user named "(.*)" enables the administrator role/ do |name| put users_url( :user => User.find_by_username!(name) ), :user => {:administrator => true} end Which at least does not blow up on me. But it does not set the administrator flag to true either. -- Posted via http://www.ruby-forum.com/.
James Byrne wrote:> > When /user named "(.*)" enables the administrator role/ do |name| > > put users_url( :user => User.find_by_username!(name) ), :user => > {:administrator => true} > > end > > Which at least does not blow up on me. But it does not set the > administrator flag to true either.Arrrgh... I seem to anticipated this type of thing: #user.rb class User < ActiveRecord::Base ... def administrator=(setting=false) end ... Sigh.... -- Posted via http://www.ruby-forum.com/.
I still cannot get this to work. Tracing the actual calls in the log I see this: Processing UsersController#index (for 127.0.0.1 at 2009-01-17 18:32:54) [PUT] Parameters: {"action"=>"index", "controller"=>"users", "user"=>"2"} User Load (16.0ms) SELECT * FROM "users" Rendering template within layouts/application Rendering users/index SQL (0.0ms) SELECT count(*) AS count_all FROM "users" WHERE (last_request_at > ''2009-01-17 18:22:54'') User Load (0.0ms) SELECT * FROM "users" WHERE ("users"."id" = 1) LIMIT 1 User Update (0.0ms) UPDATE "users" SET "updated_at" = ''2009-01-17 23:32:54'', "last_request_at" = ''2009-01-17 18:32:54'' WHERE "id" = 1 Completed in 110ms (View: 94, DB: 16) | 200 OK [http://www.example.com/users?user=2] User Load (0.0ms) SELECT * FROM "users" WHERE ("users"."username" = ''myuser'') This is my users_controller/update action: # PUT /users/1 # PUT /users/1.xml def update @user = @current_user if @user.update_attributes(params[:user]) flash[:notice] = ''User Account Changes Saved'' redirect_to account_url else render :action => "edit" end end -- Posted via http://www.ruby-forum.com/.
Matt Wynne
2009-Jan-17 23:53 UTC
[rspec-users] [Cucumber, Rails] Testing arbitrary post action parameters
On 17 Jan 2009, at 23:34, James Byrne wrote:> James Byrne wrote: > >> >> I have spent a good deal of time trying to get this to work with no >> success. I have read the api regarding the put method in: >> >> ActionController::Integration::Session >> >> If I read this aright then in your example user should be a hash, >> but a >> hash of what? Where would I create it in my test and what should it >> contain? > > I ended up with this: > > :user => User.find(:first) > > When /user named "(.*)" enables the administrator role/ do |name| > > put users_url( :user => User.find_by_username!(name) ), :user => > {:administrator => true} > > end > > Which at least does not blow up on me. But it does not set the > administrator flag to true either.If you look at the Rails documentation for the named route methods, I think you might want to try something a bit more like this: user = User.find_by_username!(name) put users_url(user, :user => {:administrator => true}) If you think you know the URL you want, why not try hard-coding it for a while in the step until you get the desired effect on the controller? Then you can add a spec for your routing which proves that the call to users_url() with the parameters you''re trying gives you the same hard-coded value. Then you''ll have discovered for yourself exactly the right parameters to use to send the right PUT request all the way to the controller. Make sense? Matt Wynne http://blog.mattwynne.net http://www.songkick.com
On Sat, Jan 17, 2009 at 3:34 PM, James Byrne <lists at ruby-forum.com> wrote:> James Byrne wrote: > >> >> I have spent a good deal of time trying to get this to work with no >> success. I have read the api regarding the put method in: >> >> ActionController::Integration::Session >> >> If I read this aright then in your example user should be a hash, but a >> hash of what? Where would I create it in my test and what should it >> contain? > > I ended up with this: > > :user => User.find(:first) > > When /user named "(.*)" enables the administrator role/ do |name| > > put users_url( :user => User.find_by_username!(name) ), :user => > {:administrator => true} > > end > > Which at least does not blow up on me. But it does not set the > administrator flag to true either.Okay well it''s been a little frustrating watching this thread, after I''ve made suggestions and I don''t think you''ve tried them. So, I decided to verify this myself and I made a little demo app to see if it works... put user_url(@user), :user => {:admin => true} does in fact work. So, yeah. Last post I''m making in this thread. Also, to easily get the behavior you want, you can do class User < ActiveRecord::Base attr_protected :admin end which will ignore the admin key when bulk-updating attributes (such as using update_attributes). Pat
James Byrne
2009-Jan-18 02:47 UTC
[rspec-users] [Cucumber, Rails] Testing arbitrary post action parameters
Matt Wynne wrote:> > If you look at the Rails documentation for the named route methods, I > think you might want to try something a bit more like this: > > user = User.find_by_username!(name) > put users_url(user, :user => {:administrator => true}) >Funnily enough, I tried this exact thing before I ended up with put users_url( :user => User.find_by_username!(name) ), :user => {:administrator => true} When I used the form that Pat provided put users_url(user), :user => {:administrator => true} then I saw this: And the user named "myuser" enables the administrator role # features/ app/models/users/step_definitions/user_steps.rb:28 You have a nil object when you didn''t expect it! The error occurred while evaluating nil.to_sym (NoMethodError) /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/routing /route.rb:243:in `extra_keys'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/routing /route.rb:243:in `map'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/routing /route.rb:243:in `extra_keys'' generated code (/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_co ntroller/routing/route.rb:154):3:in `generate'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/routing /route_set.rb:339:in `generate'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/url_rew riter.rb:208:in `rewrite_path'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/url_rew riter.rb:187:in `rewrite_url'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/url_rew riter.rb:165:in `rewrite'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/base.rb :626:in `url_for'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/integra tion.rb:227:in `url_for'' (eval):18:in `users_url'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/integra tion.rb:498:in `__send__'' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/integra tion.rb:498:in `method_missing'' ./features/app/models/users/step_definitions/user_steps.rb:30:in `And /use r named "(.*)" enables the administrator role/'' features/app/models/users/user.feature:44:in `And the user named "myuser" enables the administrator role'' If I removed the enclosing parenthesis around my_user then I saw the same thing.> If you think you know the URL you want, why not try hard-coding it for > a while in the step until you get the desired effect on the > controller? Then you can add a spec for your routing which proves that > the call to users_url() with the parameters you''re trying gives you > the same hard-coded value. Then you''ll have discovered for yourself > exactly the right parameters to use to send the right PUT request all > the way to the controller. > > Make sense?Yes, and in rough form this is what I ended up doing. The step definition I ended up with appears to be the correct method signature and it is equivalent to the example you provide. It seems that the first parameter to _put_ must be a hash. Nonetheless, I cannot seem to duplicate the exact effect of an update via the /account action (yes, I have repeated the test with put account_url(... and obtained the same results as trying put users_url(... ) -- Posted via http://www.ruby-forum.com/.
James Byrne
2009-Jan-18 04:20 UTC
[rspec-users] [Cucumber, Rails] Testing arbitrary post action parameters
Matt Wynne wrote:> If you think you know the URL you want, why not try hard-coding it for > a while in the step until you get the desired effect on the > controller? Then you can add a spec for your routing which proves that > the call to users_url() with the parameters you''re trying gives you > the same hard-coded value. Then you''ll have discovered for yourself > exactly the right parameters to use to send the right PUT request all > the way to the controller.I took your advice and, after a very long session of trial and error (mostly error), I derived this formulation: post account_url, { :user=> { "administrator" => true }, :action => "update", :commit => "Update", :_method => "put" } Now, this has the following effect in the tests:> Processing UsersController#update (for 127.0.0.1 at 2009-01-17 22:58:54) > [PUT] > Parameters: {"commit"=>"Update", "_method"=>"put", "action"=>"update", > "controller"=>"users", "user"=>{"administrator"=>"true"}}That seems to be what I was trying to pass. The stuff that follows next is authlogic updating the last_request_at field in the users table:> User Load (0.0ms) SELECT * FROM "users" WHERE ("users"."id" = 3) LIMIT 1 > User Update (0.0ms) UPDATE "users" SET "updated_at" = ''2009-01-18 03:58:54'', > "last_request_at" = ''2009-01-17 22:58:54'' WHERE "id" = 3 > User Exists (0.0ms) SELECT "users".id FROM "users" WHERE ( > "users"."perishable_token" = ''TuHra3ozvoDVJLEtFQIX'' AND "users".id <> 3) > LIMIT 1 > User Exists (0.0ms) SELECT "users".id FROM "users" WHERE ( > "users"."username" = ''myuser'' AND "users".id <> 3) LIMIT 1 > User Exists (0.0ms) SELECT "users".id FROM "users" WHERE ( > "users"."email" = ''myuser at example.com'' AND "users".id <> 3) LIMIT 1This is the magic SQL statement I was trying to produce:> User Update (0.0ms) UPDATE "users" SET "perishable_token" = > ''TuHra3ozvoDVJLEtFQIX'', "administrator" = ''t'' WHERE "id" = 3This next bit seems to be right too:> Redirected to http://www.example.com/accountCompleted in 0ms (DB: 0) | 302 Found [http://www.example.com/account] User Load (0.0ms) SELECT * FROM "users" WHERE ("users"."username" = ''myuser'' ) LIMIT 1 AND... The next feature step fails as desired: When /user named "(.*)" should not be an administrator/ do |name| my_user = User.find_by_username!(name) fail if my_user.administrator end Then the user named "myuser" should not be an administrator # features/ app/models/users/step_definitions/user_steps.rb:41 (RuntimeError) ./features/app/models/users/step_definitions/user_steps.rb:43:in `Then /us er named "(.*)" should not be an administrator/'' features/app/models/users/user.feature:45:in `Then the user named "myuser" should not be an administrator'' So, I now have my failing test and can write (uncomment) the code needed to protect against it. Thanks for the help. It was extremely valuable in leading me to the right answer. -- Posted via http://www.ruby-forum.com/.
James Byrne
2009-Jan-18 05:02 UTC
[rspec-users] [Cucumber, Rails] Testing arbitrary post action parameters
James Byrne wrote:>It seems that the first parameter to _put_ must be a hash. Which is completely wrong of course, the first argument to _put_ must be a path. I confused the argument to the named route method with that of the put method. It is the argument to the named route that must be a hash as far as I can determine from experimentation. -- Posted via http://www.ruby-forum.com/.
Pat Maddox
2009-Jan-18 07:23 UTC
[rspec-users] [Cucumber, Rails] Testing arbitrary post action parameters
On Sat, Jan 17, 2009 at 6:47 PM, James Byrne <lists at ruby-forum.com> wrote:> When I used the form that Pat provided > put users_url(user), :user => {:administrator => true}The reason this didn''t work originally is because it was supposed to be user_url, not users_url. My most recent post had the fix, but I forgot to highlight that the earlier post was incorrect. Pat