Deep Dive Into Rails ActionController Strong Parameters

Parameters

The data sent with incoming request is known as parameters. The parameters in Rails can be found in the params hash and include:

  • Path Parameters: Embedded in the URL, e.g., /posts/:id.
  • Query String Parameters: Added to the URL, e.g., /posts?title=rails.
  • Form Data: Submitted via forms using POST requests.
  • JSON Data: In APIs where the request body contains JSON.

Unlike a plain Ruby hash, the params hash is an ActionController::Parameters object which treats symbols (e.g., :key) and strings (e.g., "key") as equivalent keys.

Strong Parameters

Strong parameters allow us to explicitly permit and require specific attributes in the controller, preventing mass assignment vulnerabilities.

We can manually filter parameters:

class PostsController < ApplicationController
  def create
    @post = Post.create(
      title: params[:title],
      content: params[:content]
    )
  end
end

Testcase: params = { title: "Welcome to Rails", content: "Deep dive into rails...", published: true }

Output: { title: "Welcome to Rails", content: "Deep dive into rails..." }

Simplified with Strong Parameters

The ActionController::Parameters object provides predefined methods like require, permit and expect, simplifying parameter handling.

Also, we had this strong_parameters gem that we could use in rails 3 projects.

class PostsController < ApplicationController
  def create
    @post = Post.create(post_params)
  end

  def post_params
    params.permit(:title, :content)
  end
end

This eliminates manual filtering while ensuring secure and concise code.

Why strong parameters

By default, Rails allows all parameters to be passed to the controller, which can create security vulnerabilities. Strong parameters help us to prevent mass assignment vulnerabilities.

Let us Imagine we have a post with attributes like title, content, and published (where published indicates whether the post is visible to the public).

Without Strong Parameters

Without whitelisting, creating a post with user-provided params can compromise data if unauthorized fields are included.

{
  "post": {
    "title": "Welcome to Rails",
    "content": "Deep dive into rails...",
    "published": true
  }
}
@post = Post.create!(params[:post])
INSERT INTO "posts" ("title", "content", "published", "created_at", "updated_at")
VALUES ('Welcome to Rails', 'Deep dive into rails...', TRUE, '2025-02-18 12:34:56.789', '2025-02-18 12:34:56.789');

In this case, the post is made publicly visible even if the user does not have permission to publish it, as only an admin is allowed to publish posts. The published: true field is altered without authorization, leading to a potential security issue.

With Strong Parameters

We have to explicitly define which attributes are allowed to be assigned, ensuring unauthorized fields (like published) cannot be set by the user.

@post = Post.create!(post_params)

def post_params
  params.require(:post).permit(:title, :content)
end

Even if the attacker sends this malicious request:

{
  "post": {
    "title": "Welcome to Rails",
    "content": "Deep dive into rails...",
    "published": true
  }
}

The post_params method ensures only title and content are permitted, while the published field is ignored unless explicitly whitelisted, thus preventing unauthorized publishing of the post.

INSERT INTO "posts" ("title", "content", "created_at", "updated_at")
VALUES ('Welcome to Rails', 'Deep dive into rails...', '2025-02-18 12:34:56.789', '2025-02-18 12:34:56.789');

The importance of protecting against mass assignment vulnerabilities was highlighted in 2012 when GitHub suspended a member over a ‘mass-assignment’ hack exploited by Homakov, demonstrating how critical this safeguard is for application security.

How Strong Parameters Work

When a user sends a request (e.g., via a form or API):

{
  "post": {
    "title": "Welcome to Rails",
    "content": "Deep dive into rails...",
    "published": true
  }
}

We can define allowed parameters in the controller:

def post_params
  params.require(:post).permit(:title, :content)
end
  • The require ensures the presence of a specific key (e.g., :post).
  • The permit whitelists attributes (e.g., :title, :content).

Use post_params in actions to filter parameters before passing them to the model:

@post = Post.create!(post_params) # Only :title and :content are assigned

Any unpermitted fields, like published, will be ignored, even if included in the request.

The params#expect method, which was introduced in Rails 8 for better parameter processing, enables filtering by expected types to avoid errors caused by tampering or invalid input:

params.expect(post: [ :title, :content])

For reliable, type-safe parameter processing in Rails 8 and later, the expect method is advised.

Conclusion

Strong parameters are a crucial feature in Rails, as they significantly enhance application security by offering protection against issues with mass assignment.

By explicitly whitelisting permitted parameters for each controller action, developers can prevent unauthorized modifications.

Strong parameters are also simple to construct, guaranteeing safe input handling while preserving the simplicity and clarity of the code.

Need help on your Ruby on Rails or React project?

Join Our Newsletter