Cyberattacks are happening pretty much constantly these days. Even if you follow general security best practices, there are many language- and framework-specific vulnerabilities that attackers can try to exploit. One of these vulnerabilities is the so-called path traversal vulnerability.
In this post, you'll learn what the path traversal vulnerability is and how to prevent it in the case of the Ruby on Rails application.
What's Path Traversal Vulnerability?
A path traversal vulnerability, sometimes also called a directory traversal attack, is a type of vulnerability that allows attackers to access files to which they shouldn't have access. For example, an attacker may try to access the /etc/passwd file on your host operating system.
This vulnerability happens when your web application doesn't properly validate the paths that the user is requesting. For now, we'll focus on path traversal in the context of the Rails framework.
Path Traversal in Rails
In order to understand how path traversal can happen in Rails, we need to first understand how Rails handles paths in general. There are three most common ways of serving files in Rails.
First, we have assets pipeline files. These are all the CSS, JavaScript, and image files that Rails needs for rendering the HTML code. For example, your website logo is a good candidate to be in the assets directory. Anything that's there will have a Rails-generated path assigned. So you don't access these files directly (as in www.example.com/assets/logo.jpg), but you let Rails generate and use a path for you.
Next, we have the Rails public directory. This is a folder where you can put any file that you need to serve to clients. So, for example, if you need a marketing1.jpg file accessible as www.example.com/marketing1.jpg, you can just put it into the public directory. It's worth noting here that serving files from the public directory can be done by Rails itself but can also be offloaded to a web server (for example, Apache or NGINX).
Lastly, we have dedicated libraries for serving images on your website. Depending on your Rails version, this could be handled by third-party libraries (such as Paperclip or CarrierWave) or, in the case of the latest Rails versions, natively by Active Storage.
Now, all of these different options for getting the path of a specific file will give you different results. And the possibility of a path traversal vulnerability differs for all of them. So let's get back to the topic of this post. What does the path traversal attack actually look like?
Path Traversal
You know already that publicly available files in your Rails application are located in the same folder as the rest of your application. Our goal is to only show the users the files they're supposed to see. For instance, the contents of the public folder. But you wouldn't like users to see your Gemfile or environment configuration files.
Let's look at an example. When a user opens www.example.com/example.jpg, Rails looks for example.jpg in the /app/public folder. But just two directories higher is your Gemfile (/Gemfile).
So you want to avoid the situation where a user simply opens www.example.com/../../Gemfile and is able to see your Gemfile. That's path traversal vulnerability: the ability to manipulate a URL in order to read files that shouldn't be read. Fortunately, it's not that simple, and Rails, by default, protects you from such obvious misusages.
However, attackers can try different ways of forcing Rails to go up or down directories. For example, using URL-encoding characters:
https://www.example.com/%2e%2e/%2e%2e/Gemfile
%2e is a URL-encoded version of a dot ( . ); therefore, effectively we're asking for the same path as before: ../../Gemfile. This is just an example. Fortunately, Rails will protect you from that too.
But while Rails is smart enough to protect you from most attempts, it's not bulletproof. So let's talk about preventing path traversal.
Prevention
The good news is that because of the way Rails works (you barely write raw code but you use Rails methods and helpers), you don't need to do anything specific to protect yourself from path traversal vulnerability. As we mentioned before, commonly used methods in Rails already protect you from path traversal.
The problem may arise when you don't use native Rails path-generation mechanisms or you start generating paths from user input. So the most important prevention mechanism is to not build any custom path-handling logic yourself and use Rails-native methods instead.
Vulnerable Rails
The bad news is some of the built-in Rails libraries or gems can have this vulnerability. One of the most common examples is CVE, found in actionpack.
On the other hand, if you're building a very custom application, you may find yourself in a situation where you need to, for example, integrate your application with some legacy system. Sometimes such integrations require you to handle files and directories on the file system in a non-Rails-native way.
Watch Out for User Input
One of the best ways to create a path traversal vulnerability in your application is to directly use the user input as a variable for path generation. In many cases, this bypasses default Rails security mechanisms, allowing attackers to manipulate a path that you never intended them to.
If your use case requires getting user input for building a path, make sure to validate and sanitize that input before using it as a variable for path methods.
Non-Root
Another good practice that can help you avoid a path traversal vulnerability is to run your application as a non-root user. In the case of a path traversal vulnerability, this will still allow attackers to get access to the application directory but will at least prevent them from accessing /etc or /root directories on your host machine.
Gems
Last but not least: gems. We all love gems. They drastically simplify Ruby on Rails applications development. However, they can sometimes be buggy. While the most commonly used gems are safe (or at least patched quickly in case of a found vulnerability), you should be careful when installing gems that are really old or that haven't been updated for a long time and don't have much information about them on the internet. Sometimes it's better to write a little bit of extra code yourself instead of using a gem that was last updated 10 years ago.
Summary
When it comes to the path traversal vulnerability, the bottom line is that it's a very exotic attack vector, and Rails, by default, protects you from it. However, that doesn't mean that you're safe and don't need to worry about it. By using user input without validation or when using very old Rails versions, you can still become compromised. Path traversal, in the worst-case scenario, can give an attacker full access to your machine (if, for example, they manage to read the private SSH key of the root user). Therefore, it's always a good idea to keep your Rails version up to date.
But even if you follow security best practices, there's always a chance that you'll leave one small vulnerable line of code or won't update your Rails in time. To avoid becoming exposed, you'd benefit from keeping security scanning as part of your CI/CD pipeline and having a continuous overview of your security posture. For that, you can use StackHawk. It's a modern application security tool that can help you avoid not only path traversal but many other vulnerabilities too. And if you want to learn more about application security testing, feel free to read this post.
This post was written by Dawid Ziolkowski. Dawid has 10 years of experience as a Network/System Engineer at the beginning, DevOps in between, Cloud Native Engineer recently. He’s worked for an IT outsourcing company, a research institute, telco, a hosting company, and a consultancy company, so he’s gathered a lot of knowledge from different perspectives. Nowadays he’s helping companies move to cloud and/or redesign their infrastructure for a more Cloud Native approach.