Testing arbitrary post action parameters

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.

James B. 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.

On Fri, Jan 16, 2009 at 10:00 AM, James B. [email protected]
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.

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 :slight_smile:

Pat

Pat M. 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”=>“[email protected]”,
“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?

Zach D. 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.

On Fri, Jan 16, 2009 at 12:25 PM, James B. [email protected]
wrote:

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

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 B. [email protected]
wrote:

definition. I tried this:
HTTP headers
“username”=>“myuser”,

Posted via http://www.ruby-forum.com/.


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users


Zach D.
http://www.continuousthinking.com

James B. 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.

Pat M. 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?

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

On 17 Jan 2009, at 23:34, James B. wrote:

hash of what? Where would I create it in my test and what should it

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 W.
http://blog.mattwynne.net

On Sat, Jan 17, 2009 at 3:34 PM, James B. [email protected]
wrote:

contain?
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 B. 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…

Matt W. 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:inmap’
/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:ingenerate’
/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:inrewrite_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:inrewrite’
/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:inurl_for’
(eval):18:in users_url' /usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/integra tion.rb:498:insend
/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:inAnd /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(… )

Matt W. 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” = ‘[email protected]’ AND “users”.id <> 3) LIMIT 1

This is the magic SQL statement I was trying to produce:

User Update (0.0ms) UPDATE “users” SET “perishable_token” =
‘TuHra3ozvoDVJLEtFQIX’, “administrator” = ‘t’ WHERE “id” = 3

This next bit seems to be right too:

Redirected to http://www.example.com/account
Completed 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.

James B. 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.

On Sat, Jan 17, 2009 at 6:47 PM, James B. [email protected]
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