logo

Lack of data validation - Web Service - Ruby


Need

Implementation of robust data validation and input sanitization mechanisms


Context

  1. Usage of Ruby 2.0+ for developing Ruby applications
  2. Usage of Rails for building web applications in Ruby

Description

Insecure Code Example

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

The above code is a typical Ruby on Rails controller action for creating a new user. The `params[:user]` is a hash that includes all the user-related data sent from the client side. However, this code is vulnerable due to lack of data validation. It directly takes user input from `params[:user]` and uses it to create a new User object. This allows an attacker to send any data they want, including special characters or oversized data, which can lead to various attack vectors such as SQL injection, XSS (Cross Site Scripting), or even Denial of Service (DoS) if the data is too large. For example, an attacker could send a username with SQL commands, or a bio that is several gigabytes in size. The server would then try to process this data and insert it into the database, leading to potential SQL injection or server overload. The lack of data validation and control over the size of the fields being sent to the server makes this code insecure.

Steps

  1. Implement server-side data validation to ensure that only valid data is accepted.
  2. Sanitize user input to prevent the use of special characters that can be used for malicious purposes.
  3. Implement size restrictions on the fields to prevent excessive data from being sent to the server.
  4. Use strong parameters or a similar mechanism to whitelist and validate the parameters being used in the create action.
  5. Consider implementing input validation at the client-side as an additional layer of defense.

Secure Code Example

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(:username, :email, :password).tap do |whitelisted|
      whitelisted[:username] = sanitize(whitelisted[:username])
      whitelisted[:email] = sanitize(whitelisted[:email])
      whitelisted[:password] = sanitize(whitelisted[:password])
    end
  end

  def sanitize(field)
    ActionController::Base.helpers.sanitize(field)
  end
end

The updated code now includes a private method `user_params` which uses Rails' strong parameters to whitelist and validate the parameters being used in the `create` action. This ensures that only the `:username`, `:email`, and `:password` fields are permitted, and any additional fields sent in the request will be ignored. The `user_params` method also sanitizes the input for these fields using the `sanitize` method. This method uses Rails' built-in `ActionController::Base.helpers.sanitize` method to strip out any potentially harmful HTML tags or attributes from the input. This helps to prevent attacks such as Cross-Site Scripting (XSS). The `sanitize` method is called using the `tap` method on the whitelisted parameters. This allows us to modify the whitelisted parameters in-place, ensuring that the sanitized values are used when creating the new User. Finally, the `create` action now uses `user_params` instead of `params[:user]` when creating the new User. This ensures that only the whitelisted and sanitized parameters are used, providing an additional layer of security. This code does not implement size restrictions on the fields. This would need to be done at the model level, using Rails' built-in validations. For example, you could add `validates :username, length: { maximum: 50 }` to the User model to ensure that the username is no more than 50 characters long.


References

  • 186 - Lack of data validation - Web Service

  • Last updated

    2023/09/18