In Rails 3, we can define the accessible attributes:
attr_accessible :rating, :review
In this model, there is additional user_id which is protected to prevent forgery/hacking. This value is assigned in the controller:
#review.user_id = current_user.id
If I use Firebug to manually include the user_id, it will be part of the params[:review], but of course since user_id is not defined in the attr_accessible, it wouldn't get saved into the database. Such case is rather secure.
Question 1
I read in Rails 3 In Action book, Yehuda Katz included .delete method to delete unauthorized params before further action is performed: params[:review].delete(:user_id). Should I include this as well to further secure my app, or just ignore this step?
Question 2
If I should include the method above, I would like to have something like .delete_all_except to just strip it to the allowed attributes in the params. How do I do that?
Thanks.
If enabled, Rails 3.2 will through an exception if additional mass-assignment params are sent to the model
config/application.rb
config.active_record.whitelist_attributes = true
Rather than deleting out parameters you don't want, I recommend only accepting parameters you do want:
#user.update_attributes params[:user].slice(:rating, :review)
This will only return the user params you allow.
note: in Rails 4 (coming soonish), this behavior is implemented with a DSL named strong-parameters. You can install this gem in Rails 3.2 to implement now:
#user.update_attributes params.require(:user).permit(:rating, :review)
Related
I'd not claim myself to be an expert in Rails by any stretch. One of the things that confuses me is Strong Parameters, and I've not found any really straightforward tutorials on it, with the majority of the search results dominated by hits on the rails documentation which, whilst normally accurate, I don't find in any way easy to read and can't be considered a tutorial. The github for strong params also doesn't appear to cover this.
Say I have an entity called "Resource".
class ResourcesController < ApplicationController
...
def create
#resource = Resource.new(resource_params)
if #resource.save
...
respond_with(#resource.level)
else
...
end
end
def update
if #resource.update(resource_params)
...
respond_with(#resource.level)
else
...
end
end
...
def resource_params
params.require(:resource).permit(:name, :url, :description, :level_id)
end
end
Assume I have a scaffolded form which displays the fields for name, url, description and level_id. All of the fields are mandatory. I don't know how to amend the resource_params function to ensure that name, url and level_id are mandatory when updating (or creating) a resource, but that description is optional (but should still be permitted).
I've tried removing description from the require line, and adding it on a separate line as params.permit(:description) but that has not made any difference, the field is still mandatory in the form.
Any assistance on this would be welcomed!
As I said, this is nothing to do with strong parameters. You just need to remove required: true for that field to make it optional.
I think you should think about :on
http://guides.rubyonrails.org/active_record_validations.html#on
validates :email, uniqueness: true, on: :create
It enables validations only on specified action.
Strong parameters are used to ensure that no other data can pass into your object, that no1 can insert not wanted data. For example if you have entity User and it has field admin that if it is true, the user has admin role, it can not be set using the create or update method inside your controller, because some1 can send any kind of parameters to your controller, and you want to filter them (Allow only specific parameters).
This does not mean if you put something inside strong parameters that it is required for create, update or whatever. Strong parameters only are there to filter the data, and allow only certain parts of your entity to be settable through this service or REST. If you send parameters that are not in strong params defined, in your server console/log there will be unpermited parameters: list of the parameters.
I am trying to follow a ruby on rails scaffold example.
I am using ruby version: 2.1.5 with rails version : 4.1.8
I have used this command:
rails generate scaffold User name:string email:string
This worked fine - I can run the example which shows crud functionality to my generated scaffold. The examples I have been following advise to look at the model file generated. The ones in the examples seemed to have a value called attr_accessible - but for me this was not generated - I do not know why - it seems to be quite useful so you can easily see what is inside your model? My model looks like this:
class User < ActiveRecord::Base
end
I altered it to look like this :
class User < ActiveRecord::Base
attr_accessible :name, :email
validates :name, :presence=>true
end
and when I now visit localhost/users I get the error:
Can somebody please tell me how a generated model can be created with this attr_accessible line, and how I can add an example of validation to the model.
Rails 4 doesn't use attr_accessible; it uses Strong Parameters.
They both serve as a guard for mass assignment of params, often times used during form submissions.
The scaffold doesn't support automatically setting strong params as far as I'm aware, in part because people implement strong params white listing very differently.
A couple more links on Strong Params
How is attr_accessible used in Rails 4?
https://github.com/rails/strong_parameters
https://github.com/elabs/pundit#strong-parameters <= I strongly recommend this gem
To me this suggests whatever guide you're following is out of date.
I think the problem is that the scaffolding that you have used is not compatible with how Rails works in later versions. In earlier versions of Rails, attr_accessible was used for preventing security problems related to mass assignment. In later versions, the countermeasure changed and moved to the controller instead of the model.
If you still want to use the attr_accessible macro, you can add gem 'protected_attributes' to your Gemfile and install with bundler.
You shouldn't add them in the model.
The stuff you wanna access goes into the controller, like
def index
#users = User.all
end
def show
#user = User.find(params[id])
end
...
private
def user_params
# It's mandatory to specify the nested attributes that should be whitelisted.
# If you use `permit` with just the key that points to the nested attributes hash,
# it will return an empty hash.
params.require(:user).permit(:name, :mail)
end
so you can use them afterwards in your views.
e.g. in app/views/users/show...
<h1>#user.name</h1>
<p>#user.email</p>
I am starting with Rails 4. Had came across to the new security feature strong parameters related to permitting parameter in a controller.
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
This is fine, but we need to list down all the fields from the models. Is there a easy way by which listing fields down the is not required.
Thanks.
One shortcut you can do if you have say around 20 columns in your model and you don't need to specify all columns inside permit in your controller then it as:
params.require(:person).permit!
This will permit all the attributes from your model
Here's a quick "getting started" tip regarding cases where you have "lots" of fields for your model Foo... rather then whitelist them all, auto-generate a list then remove the ones you know should not be mass-assigned.
In rails console you can get a symbol list of all fields very easily, copy/paste it to the permit(...) method, then remove the ones that should not be mass-assigned like :id, :ctreated_at, :admin, etc
Foo.attribute_names.map(&:to_sym).sort
> [:address, :admin, :created_at, :id, :name, :updated_at]
This takes only a few seconds longer than using the .permit! approach, but gives you a better starting point from a security point of view.
Strong Parameters were introduced in Rails 4:
It provides an interface for protecting attributes from end-user
assignment. This makes Action Controller parameters forbidden to be
used in Active Model mass assignment until they have been whitelisted.
Basically, it means only certain param values will be sent through your controller to the model (thus allowing you more control over which data is handled by Rails)
DRY
If you're wanting to use strong params for multiple controllers, or just want to DRY up the process, we use this:
#app/controllers/application_controller.rb
private
#Strong Params
def permitted_params
#resource = self.resource_class
#model = "#{#resource}".downcase.to_sym
attributes = #resource.attribute_names + %w(custom items here)
params.permit(#model => attributes)
end
My class has a column secret which I generate using a specific process. Because of this, I don't want to let the user update this field via an edit form.
However, if the user adds manually the secret tag to the form and submit it, my object's secret gets updated too.
def update
object.attributes = params[:my_class]
end
I guess I create a before filter like this one
before_filter :clear_secret, :only => :update
def clear_secret
params.delete(:secret)
end
I would like to know if there is a better way to do this. Could I do this from the model? Is there a already existing Rails method for this?
If you're using Rails 3, you can protect this via attr_protected
class YourModel < ActiveRecord::Base
attr_protected :secret
end
A user attempting to inject a secret field into the form will instead cause Rails to raise a ActiveModel::MassAssignmentSecurity::Error.
However, this mechanism is deprecated in Rails 4 in favor of the strong_parameters mechanism referenced by #gylaz.
It's conventional to use strong_parameters gem to do this.
Using said gem you can permit only the attributes that can be updated, like this:
params.permit(:some_attr, :another_attr)
I'm a bit of a nube, and I am trying to piggyback on bokmann's rails3_fullcalendar to create a calendar app in rails 3.2, but when I try to create an event in my app I get this error:
ActiveModel::MassAssignmentSecurity::Error in EventsController#create
Can't mass-assign protected attributes: title, description, starts_at(1i), starts_at(2i),
starts_at(3i), starts_at(4i), starts_at(5i), ends_at(1i), ends_at(2i), ends_at(3i), ends_at(4i),
ends_at(5i), all_day
{"utf8"=>"✓",
"authenticity_token"=>"bq3ZUXLm4lYbja9FUafbroFF2Zwt8iMw6GWfvoRuPLA=",
"event"=>{"title"=>"sddfsdf",
"description"=>"df",
"starts_at(1i)"=>"2012",
"starts_at(2i)"=>"6",
"commit"=>"Create Event"}
The solution looks like it should be this but passing the AUTH_TOKEN with the AJAX POSTs (which it is sending) but it is still not working.
Any ideas?
The answer to your specific question is below, but a better answer is to look at the new version of that demo. Since the demo you're looking at, I have recreated the demo showing how to do this in Rails 3.2 with the fullcalendar JavaScript and css as an asset gem:
https://github.com/bokmann/fullcalendar_assets
Sometime recently (Rails 3.2?) the defaults for protection against mass assignment were changed... you now need to specifically allow the attributes that you want to allow mass assignment.
http://guides.rubyonrails.org/security.html#mass-assignment
In the event model, add a line that looks like this:
attr_accessible :title, :description, :starts_at, :ends_at, :all_day
If you get this Exception, you should already be authenticated, so the Auth_token is not your problem.
Rails has since version 3.2.3 a default to protect against mass-assignments.
You need to explicitly allow them in the model. That is why older code from third parties will fail. Change the model to:
class Event
..
attr_accessible :title, :description, starts_at ...
end
But do not include things like user_id into the list of allowed attributes, this way you prevent somebody who is only allowed to change her own events to reconnect the event that it will then to belong to another user.
See also the Rails Guide:
Security Guide, Mass assignment
The fullcalendar app appears to be created before config.active_record.whitelist_attributes = true became a rails default.
In your Event model, do you have a line like
attr_accessible :title, :description etc.?