Strong Parameters in Rails — Mass Assignment Protection
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
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