Skip to content

Strong Parameters in Rails — Mass Assignment Protection

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Strong Parameters in Rails. We cover key concepts, practical examples, and best practices to help you master this topic.

Rails strong parameters whitelist request parameters to prevent mass assignment vulnerabilities, allowing only explicitly permitted attributes through to the model.

What You'll Learn

By the end of this tutorial, you'll use permit and require for parameter whitelisting, handle nested and array parameters, implement conditional permissions, and test strong parameters.

Why Strong Params Matter

Without strong parameters, a malicious user could set admin=true or role=admin through a form submission. Strong params create an explicit allowlist of permitted parameters.

Real-World Use

A registration form permits name, email, and password but prevents setting admin or role. An API permits nested attributes for order items within an order.

Strong Params Path

flowchart LR
  A[Rails Controllers] --> B[Strong Parameters]
  B --> C[permit]
  B --> D[require]
  B --> E[Nested]
  B --> F[Scopes]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Basic Permit and Require

Core strong parameters usage.

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render :new
    end
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :password, :password_confirmation)
  end
end

Nested Parameters

Whitelist nested attributes.

class OrdersController < ApplicationController
  private

  def order_params
    params.require(:order).permit(
      :customer_id,
      :notes,
      :shipping_address_id,
      items_attributes: [
        :id,
        :product_id,
        :quantity,
        :unit_price,
        :_destroy,              # Allow nested destruction
      ],
    )
  end
end

# Usage in form
# params = { order: { items_attributes: [{ product_id: 1, quantity: 2 }] } }

Array and Scalar Permissions

Handle arrays and specific types.

class ProductsController < ApplicationController
  private

  def product_params
    params.require(:product).permit(
      :name,
      :price,
      :description,
      tags: [],                    # Array of strings
      image_ids: [],               # Array of integers
      metadata: {},                # Hash - any keys permitted
    )
  end
end

# Usage
# params = { product: { tags: ["sale", "new"], image_ids: [1, 2, 3] } }

Conditional Permissions

Permit different params based on user role.

class ProductsController < ApplicationController
  private

  def product_params
    permitted = [:name, :description, :price]
    if current_user.admin?
      permitted += [:featured, :published, :featured_at]
    end
    params.require(:product).permit(permitted)
  end
end

# Or with custom method
def product_params
  params.require(:product).permit(permitted_attributes)
end

def permitted_attributes
  base = [:name, :description, :price]
  base += [:published, :featured] if current_user&.admin?
  base += [:wholesale_price] if current_user&.manager?
  base
end

Strong Params in API

Handle JSON API parameter conventions.

class Api::V1::UsersController < ApplicationController
  def create
    # JSON API sends: { data: { type: "users", attributes: { name: "..." } } }
    user_params = params.require(:data).require(:attributes).permit(:name, :email)
    @user = User.new(user_params)
    if @user.save
      render json: @user, status: :created
    else
      render json: { errors: @user.errors }, status: :unprocessable_entity
    end
  end
end

# With included relationships
def create
  attributes = params.require(:data).require(:attributes).permit(:name, :email)
  relationships = params.dig(:data, :relationships) || {}
  if relationships["organization"].present?
    org_id = relationships.dig("organization", "data", "id")
    @user = User.new(attributes.merge(organization_id: org_id))
  end
end

Common Mistakes

1. Using permit! Which Permits Everything

permit! allows all params. Never use it except for testing.

2. Forgetting require and Relying on permit Alone

Without require, params without the expected key silently pass as nil.

3. Not Permitting _destroy for Nested Forms

Nested forms with _destroy need explicit permission in items_attributes.

4. Over-Permitting in API Controllers

API controllers should permit only what the client needs to send, not all model attributes.

5. Using fetch Instead of require

fetch(:user) does not validate presence. require raises ActionController::ParameterMissing for missing keys.

Practice Questions

1. What does params.require(:user) do?

Raises ParameterMissing if :user key is missing. Returns the value if present.

2. How do you permit an array of strings?

Use permit(tags: []) in the params whitelist.

3. How do you handle nested has_many attributes?

Use permit(items_attributes: [:id, :name, :_destroy]).

4. What is the difference between permit and require?

require validates key presence. permit whitelists allowed attributes.

5. Challenge: Write strong params for a blog post with tags and comments.

def post_params
  params.require(:post).permit(
    :title, :body, :category_id,
    tag_ids: [],
    comments_attributes: [:id, :author, :body, :_destroy],
  )
end

FAQ

What happens if I do not use strong parameters?

Any model attribute can be mass-assigned, including protected attributes like admin.

Can I use strong parameters outside controllers?

Yes. ActionController::Parameters.new(hash) works anywhere.

What is the difference between permit and permit!

permit whitelists specific keys. permit! permits everything (dangerous).

How do I permit a hash with unknown keys?

Use permit(metadata: {}) to allow any keys in a hash.

Can strong parameters handle nested arrays?

Yes. Use permit(items: [[:id, :name]]) for arrays of hashes.

Mini Project: Secure User Registration

Build strong parameters for secure user registration.

class RegistrationsController < ApplicationController
  def create
    @user = User.new(registration_params)
    if @user.save
      sign_in @user
      redirect_to root_path, notice: "Welcome!"
    else
      render :new, status: :unprocessable_entity
    end
  end

  private

  def registration_params
    params.require(:user).permit(:name, :email, :password, :password_confirmation)
  end
end

What's Next

Rails Controllers Deep Rails Views Deep Rails Layouts Partials

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro