Skip to main content

Client-side template injection

Need

Prevention of client-side template injection attacks

Context

  • Requirement of Ruby 2.0 or higher for running the application
  • Usage of Rails framework for building web applications

Description

Non compliant code

class UsersController < ApplicationController
def show
@user = User.find(params[:id])
render inline: params[:template], type: :erb
end
end

In the above code, the show method in UsersController is vulnerable to client-side template injection.

This method retrieves a user from the database using the id parameter from the request and assigns it to the @user instance variable. Then, it renders a template that's directly taken from the template parameter in the request.

This means that an attacker can pass in a malicious ERB (Embedded Ruby) template in the template parameter of the request. This ERB template can include Ruby code, which will be executed on the server when the render inline: params[:template], type: :erb line is run.

This is a serious security vulnerability because it allows an attacker to execute arbitrary Ruby code on the server, potentially leading to data leakage, data corruption, denial-of-service, or even full system compromise.

Steps

  • Sanitize user input: Validate and sanitize the 'template' parameter before rendering it. This can be done by using a whitelist approach, allowing only specific safe template syntax.
  • Avoid rendering user input directly: Instead of rendering the 'template' parameter directly, consider using a predefined set of templates or templates stored in the server-side.
  • Implement server-side rendering: Move the rendering logic to the server-side to prevent client-side template injection. Generate the template on the server and send the rendered result to the client.
  • Use a secure template engine: If possible, use a template engine that has built-in protection against template injection vulnerabilities.
  • Regularly update dependencies: Keep the Ruby on Rails framework and its dependencies up to date to benefit from security patches and fixes.
  • Educate developers: Provide training and guidelines to developers on secure coding practices, including the risks associated with client-side template injection.

Compliant code

class UsersController < ApplicationController
TEMPLATES = ['template1', 'template2', 'template3'] # predefined set of templates

def show
@user = User.find(params[:id])
template = params[:template]
if TEMPLATES.include?(template)
render inline: template, type: :erb
else
render inline: 'Invalid template', type: :erb
end
end
end

The updated code now includes a predefined set of templates (TEMPLATES) that are considered safe to render. When the show action is called, it checks if the provided template parameter is included in the TEMPLATES array. If it is, the template is rendered. If it's not, an 'Invalid template' message is rendered instead.

This approach is known as a whitelist approach, where only specific, known safe inputs are allowed. This prevents an attacker from injecting malicious payloads into the template, as the application will only render templates that are already defined in the TEMPLATES array.

In addition, the rendering logic is still kept on the server-side, which prevents client-side template injection. The server generates the template and sends the rendered result to the client.

It's also recommended to keep the Ruby on Rails framework and its dependencies up to date to benefit from security patches and fixes, and to provide training and guidelines to developers on secure coding practices, including the risks associated with client-side template injection.

References