Skip to main content

Lack of data validation - Web Service

Need

Implementation of robust data validation and input sanitization mechanisms

Context

  • Usage of Ruby 2.0+ for developing Ruby applications
  • Usage of Rails for building web applications in Ruby

Description

Non compliant code

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

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

Compliant code

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