I don't really understand the purpose of params.require - more specifically, what the use is for it to raise an exception when a param is missing.
I know I might want some params to be present for a given request, but why would I want to return a 500 server error if any of them are missing? Why wouldn't I traverse params for the parameters I want using conditional logic and return flash[:error] instead?
This is due to Rails using Strong Parameters, see Strong Parameters. Ive pasted the section on Strong Parameters at the bottom of the answer
Unlike other platforms that whitelist everything and require blacklisting of parameters, Rails does the opposite. Everything is blacklisted and you are required to whitelist the parameters you wish to allow through mass assignment.
Hence why if you try to save an object and haven't permitted a certain param, you will see a "Unpermitted parameters:" in your log/console.
Its a best practice to perform save/update actions through requiring and whitelisting params you wish to permit.
Strong Parameters
With strong parameters, Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted. This means you'll have to make a conscious choice about which attributes to allow for mass updating and thus prevent accidentally exposing that which shouldn't be exposed.
In addition, parameters can be marked as required and flow through a predefined raise/rescue flow to end up as a 400 Bad Request with no effort.
class PeopleController < ActionController::Base
# This will raise an ActiveModel::ForbiddenAttributes exception
# because it's using mass assignment without an explicit permit
# step.
def create
Person.create(params[:person])
end
# This will pass with flying colors as long as there's a person key
# in the parameters, otherwise it'll raise a
# ActionController::ParameterMissing exception, which will get
# caught by ActionController::Base and turned into that 400 Bad
# Request reply.
def update
person = current_account.people.find(params[:id])
person.update!(person_params)
redirect_to person
end
private
# Using a private method to encapsulate the permissible parameters
# is just a good pattern since you'll be able to reuse the same
# permit list between create and update. Also, you can specialize
# this method with per-user checking of permissible attributes.
def person_params
params.require(:person).permit(:name, :age)
end
end
Related
How can i use query parameter as a strong parameter.
This is my POST /tag method called by frontend to search posts.
def tag
if params[:category] == 'Shop'
render json: ShopPostPopulator.new(params[:search]).run
else
render json: Part.search(params[:search])
end
end
If i want to use strong parameter instead of 'params[:search]', how should I do it.
ActionController::Parameters is really just a hash like object and "strong parameters" is really just the equivalent of using Hash#slice to only allow a whitelist of attributes through. Which protects against mass assignment attacks. Beginners and often experienced Rails devs. seem to think that it magically filters and cleans the parameters. It doesn't - it just prevents you from getting a mass injection attack out of ignorance, stupidy or laziness.
Whitelisting is only needed if you are assigning a hash of parameters to a model:
User.update(
params.permit(:email, :password)
)
In this case it prevents a malicious user from for example passing role=superadmin or id=1 (as the first user is often the admin). If you are just assigning a single attribute from the params hash you don't need to use strong attributes. The major difference introduced back in 2012 is that whitelisting became manditory as an error is raised if you pass a ActionController::Parameters object without the #permitted = true attribute to .new, .update, .create and the other methods that spawn or update records.
If you want to though you can use ActionController::Parameters#permit to ensure that the parameter is a simple scalar type (not a hash or array):
params.permit(:search).fetch(:search, nil)
If search is an optional parameter with nested keys you can whitelist it like so:
params.fetch(:search, {}).permit(:foo, :bar)
You can also make the parameter required so that a ActionController::ParameterMissing exception is raised if its missing:
params.require(:search).permit(:foo, :bar)
Which is what you do 99% of the time in Rails since it bails early if we can't do anything meaning with the request.
I am having trouble with strong parameters in my custom controller. I understand how to use strong parameters in a new or update action. However, I can't seem to figure out if this is an unsecure use of the params[] hash or not in my custom action.
My view redirects to a controller with an order id and an operation number:
link_to 'Confirm', confirmpayment_confirm_path(order: order, operacion: order.operacion), :data => { confirm: 'Are you sure?' }
My confirmpayment controller is as follows:
class ConfirmpaymentController < ApplicationController
before_action :authenticate_user!
def lookup
authorize! :lookup, :confirmpayment
#orders=Order.where(:status => 'PENDING')
end
def confirm
authorize! :confirm, :confirmpayment
#order=Order.find(params[:order])
#order.payment_id = params[:operacion]
#order.confirm_payment_date = DateTime.now()
#order.save
end
def order_params
params.require(:order).permit(:order, :operacion)
end
end
The question is:
I am not using order_params anywhere in my confirm action, since this is not a new order. I am using the parameter to find the proper order and confirm it. Is this secure? Or am I missing something?
So to clear a few things up.
Strong Parameters is responsible for the allowed parameters which are passed to your database. It should prevent the users to modify attributes in the database which they aren't allowed to modify.
For example:
You have the following table with columns:
User:
- firstname
- lastname
- email
- password_digest
- role (user, admin)
You probably want to prevent normal users to change their role. But if you pass a parameters hash as it is to the database, he could also add a role key with value to the hash. Your application would accept it. Strong parameters checks the hash and prevent the change.
In your example above, Strong Parameters brings no advantages. You assign the values directly to the appropriate table columns. It isn't possible to modify any other attributes in the database for the user. If you don't have any other methods in your controller, you could remove the entire #order_params. Strong Parameters just raises an exception if you would try to pass a hash directly through.
However I would recommend you to search for the payment in the database before you assign it. If you assign payment_id directly you have no guarantee that payment exists. So first check for the payment and if you found it assign it to the order.
Here is how I would have done it:
class PaymentConfirmationController < ApplicationController
before_action :authenticate_user!
before_action :authorize_user! # To DRY up your code
def lookup
#orders = Order.where(:status => 'PENDING')
end
def confirm
#order = Order.find(params[:order_id])
#payment = Payment.find(params[:operation_id])
# You should catch the exceptions if an order or payment wasn't found
#order.payment = #payment
#order.payment_confirmation_date = Time.now()
#order.save
end
private
def authorize_user!
authorize! :confirm, :confirmpayment
end
end
I haven't tested the code but it should work.
Here are the Docs of Strong Parameters. They describe everything in more detail.
I hope this helps!
Happy coding :)
About the way of using parameter, I think there is nothing wrong with it. But about the security problem, you may want to think about the case that user can change any Order information by just change the order param to something that doesn't belong to him.
In that case, you will want to limit the query to Order, make he can only confirm the order that belongs to him.
Strong Parameters: prevent accidentally exposing that which shouldn't be exposed. They are generally used when you create or update a model, this to avoid entering parameters have not been allowed.
I have some suggestions:
English preferably encoding: operacion to operation
Check style
in your code ConfirmpaymentController to class
ConfirmPaymentController
you can see: best practices and style prescriptions for Ruby on Rails 4 :)
Basically, as my Rails 4.2 applications become more complicated, I sometimes forget to include the appropriate parameters in the user_params strong parameters hash. This leads to information not being saved. For example, I might forget to include the :nickname as per below code.
what's a good way to test that I have included the whitelisted parameters? I don't care if they are nil or some other value, I just need a failing test to remind me to include the parameter.
def user_params
params.require(:user).permit(:id, name, user_nested_attributes: [:nickname, :pet])
end
In rails 4.x, strong_parameters require parameters to be explicitly permitted. Yet, in the following example, I do NOT get a ForbiddenAttributesError - why does :id not throw when in the show action even though it is not explicitly permitted?
def FooController
...
def show
#foo = Foo.find(params[:id]) # why no exception here?
end
private
def foo_params
params.require(:foo).permit(:name, :address) # note: No :id here
end
end
See: http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters
"With strong parameters, Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted."
Doing a find is completely valid, and is, in fact, shown in the example in the documentation linked to, above.
Strong parameters are used only for assignment of attributes. You can freely search and perform other operations with any param, just not mass assignment.
You can see more in-depth explanation and examples in Rails Guides
For Rails, params[:id] outside from default params.
Query string:
www.example.com/foo/123?bar=1&baz=2
Request path:
www.example.com/foo/123 where 123 is params[:id]
Paramerts:
bar=1&baz=2 this can be permitted
If you pass 123 to parameters then you need permitted :id.
There is no need of explicitly permitting the :id unless you want to.Rails will do it implicitly.If want to check whether the :id is whitelisted or not,you can do puts params[:foo] after it is created or you can just see the log.you will see something like this
{id=>some_id, "name"=>"some_name", "adddress"=>"some_address"}
So,defining a Foo object like this
#foo = Foo.find(params[:id])
will not throw an exception.
Hope it helped!
I'm a bit confused about this mass assignment issue. Here's my question
Say I have a user model with the following attributes:
name
login
password
email
During an edit, the update method is triggered:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
....
end
In my head it makes sense to protect most, if not all, of these attributes as i wouldn't want the password/email/login to be compromised. So I would do this in the model
attr_accessible :name
So every other attribute, asides from name, wouldn't be able to be mass assigned.
If I do this how would the valid edit form work though? Do I need to assign attributes one by one in the update method #user.email = params[:user][:email], etc? Or am I misunderstanding something (probably)?
Thanks!
Edit:
To be more specific:
Usually you see examples with the admin attribute protected. And it makes sense.
But what about the password or email attributes? Those aren't usually protected. Why wouldn't the password be protected or the email? It could mean that potentially somebody could reset the email and do a password reset or reset the password attribute and gain access to the system, no?
Watch this railscasts http://railscasts.com/episodes/26-hackers-love-mass-assignment/
You are thinking about mass assignment security the wrong way. attr_accessbile does not make the password value open to the public (you will use filter_parameter to hide that value).
Think of it this way, you have a user form. You want the user to be able to create an account with a password but you do not want them to be able to add themselves as an admin (they could do this through sql injection or manipulating the POST parameters). To protect against this, you would add :name, :password, :email to attr_accessible and leave out the admin field.
The idea is to filter the params in your controller, as described here.
class PeopleController < ActionController::Base
# This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
# without an explicit permit step.
def create
Person.create(params[:person])
end
# This will pass with flying colors as long as there's a person key in the parameters, otherwise
# it'll raise a ActionController::MissingParameter exception, which will get caught by
# ActionController::Base and turned into that 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap do |person|
person.update_attributes!(person_params)
end
end
private
# Using a private method to encapsulate the permissible parameters is just a good pattern
# since you'll be able to reuse the same permit list between create and update. Also, you
# can specialize this method with per-user checking of permissible attributes.
def person_params
params.required(:person).permit(:name, :age)
end
end