Rails XML External
Entities Guide: Examples
and Prevention

stackhawk

StackHawk|March 4, 2022

Learn what XML External Entities are, how to spot them, and how to protect Ruby on Rails applications against this vulnerability.

In this article, we will address the subject of XML External Entities. We'll briefly define what XML External Entities are, show you how to spot them, and demonstrate how to protect Ruby on Rails applications against this vulnerability. Additionally, we will examine the common errors you might encounter while implementing mitigation tactics and help you address them accordingly. 

If you happen to have no experience developing in Ruby on Rails or you're just testing the waters, we strongly recommend you take some time to explore this article and get acquainted with it.

We'll address some features that might not be immediately clear unless you have some background in Rails. 

Rails XML External Entities (XXE) Guide: Examples and Prevention image

What Are XML External Entities?

XML, or Extensible Markup Language, is a markup language and file format for storing, transmitting, and reconstructing arbitrary data. This language is used in the programming world to define rules for encoding documents in a format that is both human-readable and machine-readable. How is this file structure vulnerable to attacks, and why does it pose a threat to your system? By default, most XML processing tools allow the specification of an external entity, a URI, that is retrieved and processed during the parsing of the XML file. When this happens, the parser can request and include the content at the specified URI inside the XML document. 

This vulnerability opens up the system for exploitation. A malicious actor, for example, could use this property as an avenue to retrieve any resource on the server. 

An XXE (XML External Entities) injection is an attack that takes advantage of XML parsing vulnerabilities. It targets systems that use XML parsing functionalities that face the user and allow an attacker to access files and resources on the server. Attacks can include disclosing local files that contain sensitive data, such as passwords or private user data and using file: schemes or relative paths in the system identifier. 

Clearly, a determined attacker could potentially take over your server, especially with a sufficient understanding of server structures and some information about the technology stack you're using.

XML External Entities Examples

Now that we have explained the concepts behind XML External Entities attacks, let's go over some examples. 

Here's a sample XML document containing a username XML element. 

<?xml version="1.0" encoding="ISO-8859-1"?>
<username>John</username>
</xml>

Pretty harmless and straightforward. Now, how do you add an external entity to your XML? Well, it's pretty simple.  

An external XML entity can be added using a system identifier within a DOCTYPE header. This directive basically adds a few more properties to the XML file structure. For example, the code below contains an external XML entity that would fetch the content of /secrets.yml and display it to the user rendered by username.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///secrets.yml" >]>
<username>&xxe;</username>
</xml>

As you can see, these entities can access local or remote content within your server. This is bad news if you keep sensitive files that can provide a path to surrender the control of your whole platform to the attacker. Other XML External Entities attacks can access local resources that may not stop returning data, possibly impacting application availability and leading to denial-of-service (DoS) attacks. 

If you're paying attention, this is when you should sound the alarm and call your security specialists. 

This is but a brief explanation of the subject of XML External Entities. By no means does it cover all complexities and intricacies. Protecting your platform against the most sophisticated exploits on the web requires an extensive understanding of the technology and a solid grasp of your platform's infrastructure. 

OK? Moving on.

Preventing XML External Entities Attacks

Alright, so how can you prevent your system from falling victim to these malicious attacks and, in the process, not lose your job? 

Don't worry. Mitigating XML External Entities attacks is pretty simple. Generally, as long as you are not intentionally trying to open a window for the vulnerability and taking into account that you need the functionality of loading user-provided XML files, Rails does a great job protecting you. 

Let's illustrate. 

As we have mentioned, if an application has an endpoint that parses XML files, an attacker could send a specially crafted payload to the server and obtain sensitive files. The files the attacker can access depend on how you set up your system and how you implement user permissions. 

So, to prevent this situation from playing out, first, don't use libraries that support entity replacement like LibXML. Instead, use the built-in default library included in Rails REXML. 

Now, if you are using this library already and changing it isn't possible, or if a critical library requires this library, make sure that entity replacement is disabled.  

You can do this by simply adding the following code in an initializing file:

require 'xml'
require 'libxml'

# Change the ActiveSupport XML backend from REXML to LibXML
ActiveSupport::XmlMini.backend = 'LibXML'

# Deny entity replacement in LibXML parsing
LibXML::XML.class_eval do
  def self.default_substitute_entities
    XML.default_substitute_entities = false
  end
end

Thankfully, new versions of LibXML make it hard to enable entity replacement. However, it's essential to keep in mind that if you decide to go this route, you may still be vulnerable to a DoS attack when using LibXML. 

Now, suppose your application actually makes use of external entities for some critical functionality. In that case, one approach you can take to minimize the potential for exploits is to safelist known external entities. 

You can do this simply by checking the XML file document before parsing it with the library for any strings containing any 'ENTITY' not on the list.

require 'nokogiri'
require 'active_record'

ActiveRecord::Base.establish_connection(
  adapter:  'mysql2', # or 'postgresql' or 'sqlite3' or 'oracle_enhanced'
  host:     'localhost',
  database: 'your_database',
  username: 'your_username',
  password: 'your_password'
)

class Job < ActiveRecord::Base
end

class ExampleData
  attr_reader :file

  def initialize(file)
    @file = file
  end

  def xml
    raise StandardError.new "POTENTIAL ATTACK" if "<!ENTITY".in? file
  
    Nokogiri::XML(open(file))
  end

  def create_jobs
    xml.css('request').each do |node|
      Job.create(
        :last_name => node['name'],
        :telephone => node['phone'],
        :street_address => node['address'],
        :city => node['city'],
        :state => node['state'],
        :zip => node['zip'],
        :email => node['email'],
        :au_chog => node['chogAu'],
        :person_type => node['affil'],
        :research_use => node['use'],
        :subject => node['subject'],
        :notes => node['note'],
        :start_date => node['startDate'],
        :end_date => node['addDate'],
        :complete => true,
        :time_spend => node['hours']
    )
    end
  end


  file = File.dirname(__FILE__) + "/data/old_data.xml"
  example_data = ExampleData.new(file)
  example_data.create_jobs
end

Nuclear Option

Finally—and I want to make sure to emphasize this—do not parse XML if it's not an application requirement. I know it might be convenient and allow the platform to provide convenient features for the users. Still, there are many ways to offer similar functionality without using these libraries. In the end, the best mitigation strategy is to not be vulnerable in any way. 

Nowadays, providing robust and secure platforms and services to users is incredibly complex. It demands considerable amounts of work and expertise. This can be taxing on a team that's laser-focused on productively delivering fast and creative solutions. 

So, for these teams, we recommend our Dynamic Application Security Testing suite. Our DAST tool tests a running version of your application to identify potential security vulnerabilities. This will allow you to have real-time, reliable, and detailed reports about the security status of your platform. 

You can check it out here.

Ready to Test Your App

Moving Forward

At this point, most development kits, packages, and libraries are pretty robust. They have many layers of protection against a myriad of exploits like XXE and others. However, there is always the possibility to unintentionally introduce a vulnerability, compromising your work and your company. Thus, it is essential to keep yourself updated and rely on the fantastic Rails community to expand your options.  

Rails is an approachable and friendly platform for building robust and flexible applications. It makes the work of securing against XXE injection exploits straightforward. No need to fiddle with complicated configurations or risky settings. 

If you want to learn more about securing your platform from threats, you can find more articles on our blog

Additionally, we would like to remind you to consider our all-encompassing security analysis solution StackHawk for more advanced and robust enterprise-level solutions. 

This post was written by Juan Reyes. Juan is an engineer by profession and a dreamer by heart who crossed the seas to reach Japan following the promise of opportunity and challenge. While trying to find himself and build a meaningful life in the east, Juan borrows wisdom from his experiences as an entrepreneur, artist, hustler, father figure, husband, and friend to start writing about passion, meaning, self-development, leadership, relationships, and mental health. His many years of struggle and self-discovery have inspired him and drive to embark on a journey for wisdom.


StackHawk  |  March 4, 2022