Tuesday, July 23, 2013

Controller Parameter Checking

Reference: rails/strong_parameters
Reference: active_record_validations.html#format
Reference: activemodel
Reference:

There are two main use cases.  The first to make sure that no extra parameters are accidentally passed into create/update operations.  This is to address any "mass assignment" vulnerability.

The second case is that RESTful controllers often have parameters not contained in the URL and hence not checked by the routing logic.  To validate those, we first construct a white-list:

nparams = params.permit( :my_param_a, :my_param_b, ...)

Note these are somewhat validated as being a basic type (see the reference above), but they aren't fully validated.

raw_parameters = { :my_param_a => "john@example.com", :my_param_b => "John", :admin => true } parameters = ActionController::Parameters.new(raw_parameters) puts "params: #{parameters}" cleaned = parameters.permit( :my_param_a,:my_param_b) puts "cleaned: #{cleaned}"
When executed:
params: {"my_param_a"=>"john@example.com", "my_param_b"=>"John", "admin"=>true} cleaned: {"my_param_a"=>"john@example.com", "my_param_b"=>"John"}
Oddly, input validation is not considered part of a controller; it seems to be model logic (see how-can-a-controller-manually-set-validation-errors-for-a-certain-field).  So we have to develop a Model that includes ActiveModel::Base in order to check the parameters.

Here is an example model which includes ActiveModel::Model
require 'active_record' require 'action_controller' class ParamCheck include ActiveModel::Model attr_accessor :my_param_a attr_accessor :my_param_b validates :my_param_a, presence: true, length: { minimum: 4 } validates :my_param_b, presence: true end
Some test code:
pc = ParamCheck.new puts pc.valid? pc.errors.full_messages.each do |err| puts err.inspect end
And the output:
false "My param a can't be blank" "My param a is too short (minimum is 4 characters)" "My param b can't be blank"
Using the cleaned parameters in the constructor:
pc2 = ParamCheck.new( cleaned) puts pc2.valid? pc2.errors.full_messages.each do |err| puts err.inspect end
Results in:
true

Adding checking to a controller

Each controller action is responsible for handling parameter white-list and required presence.

The idea is to define a nested class within the controller that is responsible for parameter checking. To avoid having a class per action, one ensures that named parameters are used consistently across all actions. For example, params[:email] should always be an e-mail address. Don't use names like :arg1 across multiple actions where it is an integer in one and an e-mail address in another.

The nested class then supplies validators for each parameter name. The validates should not check for presence as that was already handled by the action.

After the white-list and requires, one instantiates the CheckParams class using the cleaned-up parameters. One then checks to see if the instantiation is valid. If not, one produces a response with status 400 (bad request).

render :text => "400 Bad Request", :status => 400

No comments:

Post a Comment