Skip to main content

Insecure Processing of Data

Fixing Cross-Site Scripting

About XSS

What is Cross-Site Scripting?

Cross-site scripting (XSS) is a type of security vulnerability that allows attackers to inject malicious code into web pages viewed by other users. This occurs when an application fails to properly sanitize user input and injects untrusted data into a web page without validating or encoding it.

XSS attacks can occur in different forms, such as reflected, stored, or DOM-based, and can affect various types of web applications, including social media, e-commerce, and online banking sites.

Attackers can use various techniques, such as phishing emails, web forms, or malicious links, to trick users into visiting or interacting with web pages that contain malicious code.

Check out this video for a high-level explanation:

What is the impact of Cross-Site Scripting?

Cross-site scripting (XSS) can lead to various security threats and risks, such as:

  • Information theft: XSS attacks can steal sensitive information, such as login credentials, credit card details, or other personally identifiable information, by injecting malicious code that collects and transmits user data to attackers.
  • Account hijacking: XSS attacks can hijack user sessions by injecting malicious code that takes over user sessions, allowing attackers to perform unauthorized actions on the website or gain access to user accounts.
  • Data tampering: XSS attacks can modify or delete data on a website by injecting malicious code that alters the content of web pages, leading to data tampering, data corruption, or other types of malicious activities.
  • Malware distribution: XSS attacks can distribute malware by injecting malicious code that downloads and installs malicious software on the user's computer, leading to malware infections or other types of cyber attacks.

Overall, XSS attacks can compromise the confidentiality, integrity, and availability of data, leading to a range of security threats and risks.

How to prevent Cross-Site Scripting?

To prevent XSS vulnerabilities, you can take the following steps:

  • Input validation and sanitization: Ensure that all user input is validated and sanitized before being used to generate web pages or manipulate other data. Input validation should include validating the data type, length, and format, and filtering out any special characters that could be used to inject malicious code.
  • Output encoding: Encode all output to protect against XSS attacks. Use output encoding techniques such as HTML entity encoding, URL encoding, and JavaScript encoding to encode user input and output to the browser.
  • Use security headers: Implement security headers to protect against XSS attacks, such as Content-Security-Policy (CSP) and X-XSS-Protection. CSP can help prevent XSS attacks by allowing only trusted sources of content to be loaded on a page, while X-XSS-Protection can help prevent the browser from rendering pages that contain malicious scripts.
  • Session management: Implement secure session management mechanisms, such as session cookies, to protect against session hijacking and other attacks.
  • Secure coding practices: Use secure coding practices to prevent XSS vulnerabilities. This includes using libraries and frameworks that are designed to prevent XSS attacks, testing code for vulnerabilities, and using secure coding practices such as input validation and output encoding.
  • Regular security audits: Regularly audit your system for security vulnerabilities, including XSS vulnerabilities. Use automated tools and manual testing to identify potential issues and fix them before they can be exploited.

References

Taxonomies

Explanation & Prevention

Training

Elixir and the Phoenix Framework provide a robust foundation for developing secure web applications. By default, Phoenix uses the EEx (Embedded Elixir) templating engine which automatically escapes output in templates, providing a good layer of protection against Cross-Site Scripting (XSS) attacks. This means that if you're using EEx or Phoenix's LiveView for rendering your HTML, you are largely protected against XSS as they will automatically escape any dynamic content.

However, Elixir/Phoenix applications are not immune to XSS attacks. Caution is required when handling user input or rendering dynamic content.

Category-specific reference:

Option A: Escape the output Phoenix.HTML.html_escape()

  1. Go through the issues that GuardRails identified in the PR/MR.

  2. Look for patterns like this:

    def index(conn, %{"dangerous" => dangerous}) do
    html conn, "<h1>\#{dangerous}</h1>"
    end

    or:

    def index(conn, %{"test" => test}) do
    send_resp(conn, 200, test)
    end
  3. And use Phonenix.HTML.escape to properly escape the output:

    defmodule YourAppWeb.YourController do
    use YourAppWeb, :controller
    import Phoenix.HTML

    def index(conn, %{"dangerous" => dangerous}) do
    safe_string = html_escape(dangerous)
    html conn, "<h1>#{safe_string}</h1>"
    end
    end

    or:

    def index(conn, %{"test" => test}) do
    test = Phoenix.HTML.html_escape(test)
    send_resp(conn, 200, test)
    end
  4. Test it

  5. Ship it 🚢 and relax 🌴

Option B: Avoid use of raw()

  1. Go through the issues that GuardRails identified in the PR/MR.

  2. Look for patterns like this:

    <%= raw(@user_input) %>
  3. Remove the use of raw to securely escape the input

    <%= @user_input %>
  4. Test it

  5. Ship it 🚢 and relax 🌴

Fixing Insecure Use of binary_to_term

About Deserialization

What is a Deserialization vulnerability?

Serialization converts complex data structures into a stream of bytes that can be sent or received over a network. Deserialization restores the byte stream to a copy of the previously serialized object. The receiving application can then interact with this deserialized object.

Deserializing attacker-controlled data allows attackers to manipulate serialized objects and pass malicious data to the receiving application.

Alternative terms for Serialization/Deserialization are:

  • Marshaling, Unmarshaling
  • Pickling, Unpickling

Check out this video for a high-level explanation:

What is the impact of Deserialization vulnerabilities?

Attackers can reuse existing application code in malicious ways which results in a wide range of vulnerabilities such as:

  • Code execution: An attacker can exploit deserialization vulnerabilities to execute arbitrary code on a target system, giving them control over the system and access to sensitive data.
  • Unauthorized access: An attacker can use deserialization vulnerabilities to access and manipulate data or functionality that they are not authorized to access, such as administrative functions or sensitive data.
  • Denial-of-service (DoS) attacks: An attacker can exploit deserialization vulnerabilities to cause DoS attacks, by overloading the system with large amounts of data or by manipulating the data in a way that causes the system to crash or become unresponsive.

How to prevent Deserialization vulnerabilities?

To prevent deserialization vulnerabilities, it is important to follow security best practices and implement appropriate security measures, such as:

  • Avoid deserialization of untrusted data: Do not deserialize data from untrusted sources or unvalidated user input.
  • Use type checking and input validation: Verify the type and content of serialized data to ensure that it is valid and expected.
  • Use secure deserialization libraries and frameworks: Use secure deserialization libraries and frameworks that can help prevent deserialization vulnerabilities and provide additional security features, such as input validation and type checking.
  • Apply access controls: Apply access controls to limit the privileges and actions that deserialized data can perform, such as preventing it from executing arbitrary code or accessing sensitive resources.
  • Keep software up to date: Keep software and security protocols up to date, as new vulnerabilities and security patches are regularly released.

References

Taxonomies

Explanation & Prevention

Training

If user input is passed to Erlang's binary_to_term function it may result in memory exhaustion or code execution. Even with the :safe option, binary_to_term will deserialize functions, and shouldn't be considered safe to use with untrusted input.

Rule-specific references:

Option A: Avoid user input in binary_to_term

  1. Go through the issues that GuardRails identified in the PR/MR

  2. Identify the following patterns:

    def index(conn, %{"test" => test}) do
    :erlang.binary_to_term(test)
    end
  3. Where possible, avoid the function entirely, or only allow validated input.

    def index(conn, %{"test" => test}) do
    :erlang.binary_to_term(test, [safe])
    end
  4. Test it and ensure the functionality works as expected

  5. Ship it 🚢 and relax 🌴

Preventing Denial-of-Service

About Denial-of-Service

What is code injection?

Code injection is a security vulnerability that occurs when an application generates code dynamically and does not properly validate or sanitize user input before using it to generate the code.

The main difference between command injection and code injection is that command injection focuses on executing arbitrary system commands, while code injection focuses on injecting and executing arbitrary code within a program or system.

Code Injection is limited by the functionality of the injected language itself.

Check out this video for a high-level explanation:

What is the impact of code injection?

A successful code injection attack can have a wide-ranging impact, depending on the system and the attacker's goals. Here are a few potential impacts:

  • Unauthorized access: An attacker may be able to gain unauthorized access to a system or application, giving them access to sensitive data or functionality.
  • Data theft: An attacker may be able to steal data from the system, including personally identifiable information, financial data, or other sensitive data.
  • Malware installation: An attacker may be able to install malware on the system, allowing them to further compromise the system or use it as a launching point for other attacks.
  • Denial of service: An attacker may be able to launch a denial-of-service attack by executing code that overwhelms the system's resources.
  • System compromise: In some cases, a successful code injection attack can lead to the complete compromise of the system.

How to prevent code injection?

Some measures that can help prevent code injection attacks are:

  • Input validation and sanitization: Ensure that user input is validated and sanitized before it is used to generate code.
  • Perform regular security audits: Regularly audit your system and application for security vulnerabilities, including code injection vulnerabilities. Use automated tools and manual testing to identify potential issues and fix them before they can be exploited.
  • Educate your development team: Educate your development team about the risks of code injection attacks and the measures that can be taken to prevent them.

References

Taxonomies

Explanation & Prevention

Training

In Elixir, atoms are constant values where the name and value are the same. They are not garbage collected, meaning once an atom is created, it persists for the lifetime of the system. Creating atoms from user input can result in memory exhaustion and potential denial-of-service attacks, as there is a limit to the number of unique atoms that a system can handle.

To avoid atom exhaustion in Elixir, use the secure alternatives as shown below:

Category-specific references:

A step-by-step instruction for avoiding atom exhaustion is shown for String.to_atom below.

Option A: Avoid Atom Exhaustion with String.to_atom

  1. Go through the issues that GuardRails identified in the PR/MR.

  2. Locate the following code patterns:

    # Unsafe usage of String.to_atom
    def index(conn, params) do
    render conn, String.to_atom(params["test"])
    end
  3. And replace them with the secure alternative:

    # Safe usage of String.to_existing_atom
    # and atom interpolations
    def index(conn, params) do
    try do
    render conn, String.to_existing_atom(params["test"])
    rescue
    ArgumentError -> handle_atom_conversion_error() # Replace with your own error handling function
    end
    end
  4. Test your changes

  5. Ship it 🚢 and relax 🌴