This post’s topic is very straightforward: SQL injection, Ruby flavored. More specifically, how you can protect your Ruby application against SQL injections—and other common security threats. Ruby is a wonderful language for beginner coders to start with and scale to large, distributed Web and Desktop applications. It has an accepting and helpful community. Also, it strives to keep itself up to date to match the needs of developers.
You can add functionality to your Ruby application with gems that introduce more complexity and possibilities for exploitation into an application (especially the infamous Rails), and whilst the Ruby community had high profile security issues in the past (SQL Injections to Cross-site scripting – XSS), if you follow best practices, use recommended methods, and keep your versions up to date, you should avoid a lot of them.
However, you can’t really follow best practices and recommended methods that you don’t know about. That’s what this post is all about: getting you up to speed with said best practices. They won’t guarantee your Ruby app is 100% safe—there is no such thing as absolute security. But by following the tips outlined in this post, you can dramatically reduce the risk of your app being prey to SQL injections or other vulnerabilities.
We’ll begin the post by explaining why SQL injection can still be a problem in Ruby (especially Rails) applications. If you think SQL injections are a thing of the past…well, prepare to be convinced otherwise.
After we’re on the same page regarding the very real danger that a SQL injection (Ruby or otherwise) represents, it’s time to learn how to protect your app from this problem. We’ll give you a list of six practical tips you can adopt right away. While some of them are specifically about SQL injection, they can also make your app more resilient against different kinds of threats it may face. Let’s get to it.
SQL injection: Ruby isn’t immune to this threat
“SQL injection” is a phrase that, for many developers, invokes memories from a long, bygone era. After all, SQL injection is a solved problem. Or is it?
As it turns out, SQL injection—Ruby notwithstanding— is very much alive. The OWASP Top Ten website lists injection (which includes SQL injection along with other types of injection attacks) as the #1 security vulnerability for web applications.
And what about Ruby and Ruby on Rails specifically? Well, the first State of Application Security Report by Sqreen found out that 70% of the security exploits against Ruby on Rails were SQL injections. The data suggests that even a robust framework such as Rails isn’t enough to completely bulletproof an application against SQLi attacks. SQL injections also represent 47% of the security incidents across all of the languages analyzed, showing that SQL injections are definitely still a thing.
Escaping and sanitizing user input
You want to prevent potentially harmful input from being unintentionally executed or finding its way to your database. Ruby (and Rails) offer comprehensive escaping methods to help you with that.
The are multiple ways to sanitize and escape potentially harmful input in Ruby (and Rails). Rails version 3 and above will escape content automatically:
<%= @user.biography %>
Which is equivalent to prefixing html_escape
in earlier Rails versions and will escape any HTML to make it less harmful (but more unsightly). There are times when you still want to call the html_escape
method anyway just to be sure.
<p>I am a writer and blogger. !<script>alert(document.cookie);</script></p>
This takes you so far, if you want to be extra cautious, then you can use the Rails sanitize
method to apply filters that Rails considers safe. This includes encoding tags and stripping unallowed attributes:
<%= sanitize @user.biography %>
Great, but what if you want to allow HTML, for example, to allow users to style the text they input? Helpfully Rails allows you to add a list of acceptable tags to your application.rb file:
config.after_initialize do
ActionView::Base.sanitized_allowed_tags = 'b', 'i'
end
Or you can add and remove tags as you need them, for example:
config.after_initialize do
ActionView::Base.sanitized_allowed_tags += 'a'
ActionView::Base.sanitized_allowed_tags.delete 'b'
end
It can be easy to forget about escaping HTML when you want to generate a custom template. For example, if you use the below, malicious code could still sneak in:
module BioHelper
def block_bio(user)
"<aside>#{user.biography}</aside>".html_safe
end
end
To be safe, make sure you strip it before setting it as valid HTML:
module BioHelper
def block_bio(user)
"<aside>#{html_escape user.biography}</aside>".html_safe
end
end
Or use the methods Rails provides for generating HTML:
module BioHelper
def block_bio(user)
content_tag(:aside, user)
end
end
Databases
One of the most powerful aspects of Rails is it’s database abstraction it offers in the form of ActiveRecord for interacting with MySQL, MariaDB, PostgreSQL and SQLite databases and not having to worry about the underlying technology. Whilst Rails tries hard to prevent vulnerabilities and opportunities for SQL injection, there are still precautions you can take.
Over the web, you can find material explaining potentially harmful methods. For instance, there is this repository of all examples that I suggest you head over there for a comprehensive rundown. In summary, there are ActiveRecord methods (depending on the Rails version) that allow for arbitrary SQL. You should either prevent user input into these or sanitize the input to ensure that only values you want make it to the database.
A common potential gap for injections to sneak in is searching records, for example:
def index
@writers = Writer.all
unless params[:query].blank?
@writers = @writers.where("biography like '%#{params[:query]}%'")
end
end
Whilst this gives users a flexible search option, it does mean that they can put anything into the search query, including potentially harmful content. It’s a well-known potential vulnerability in Rails, and the solution is to instead use an array, which will sanitize the input:
def index
@writers = Writer.all
unless params[:query].blank?
@writers = @writers.where("biography like ", params[:query])
end
end
Object serialization
This vulnerability was a particularly serious one posted to the Rails security list in 2013 and was resolved in newer versions, so check that you are running 3.2.11, 3.1.10, 3.0.19, 2.3.15 and above.
Following design ideas across the Rails project, you are able to import YAML, JSON and CSV files. Rails will dump them into strings and attempt to create objects from them. As with the points above, strings can be harmful if not escaped properly. To make matters worse, earlier versions did not sufficiently escape these strings before creating objects out of them. Are you unsure if your Rails version is sufficiently up to date? The tools outlined later in this article will identify potential serialization issues for you.
Insecure gems
Gems are a wonderful source of functionality to extend your ruby applications. However, signing them to confirm identity is not a compulsory step. So, there is no guarantee of trust, exposing you to code from unknown sources and of unknown quality.
If you are a gem developer, then read the gem security guide to see how you can assure users better. If you’re not a gem developer, then encourage other developers to do the same. Only use gems you trust and have been able to find information about. This GitHub repository is also a good source of information on gems that the Ruby community considers to have security problems.
Identifying vulnerabilities
The first steps to preventing most problems with code are to create a checklist of potential issues and check for their hopeful absence. This can be a part of your testing regime or a step before testing. Ideally, you should ask someone else to check your code. Why? Spending hours staring at the same lines can cause ‘code-blindness’ and you may not notice small details anymore. Naturally, this is not a new process and there are numerous tools to help you such as Crucible, Upsource or both GitHub and GitLab are adding features to help with the process.
In my opinion, checking application code too much is not possible. So, ideally, you should test with as many methods as possible. If you are looking for guidance on whether you should trust a tool or not, then read forums, or the OWASP (Open Web Application Security Project) site, which is hard to navigate, but a great source of information.
Code analysis
As an opinionated language, Ruby has well established coding standards documented in the community-driven Ruby style guide. This is purely a guide. It covers much more than potential code vulnerabilities, though. That’s why we strongly advise you to follow it.
Checking your code manually to match the guide sounds like a daunting task. But there are several automated options, depending on what aspect of your code you want to check.
Linters provide guidance on code best practices. They belong to the domain of static application security testing (SAST) tools that analyze your code. On the other hand, dynamic application security testing (DAST) tools involve operational testing. They concern themselves with how the application actually works and how people might try to ‘break’ it. You can use these tools manually as part of your editor or local development process. Alternatively, you can use them as part of an automated testing and deployment process.
- Rubocop (SAST) – A static code analyzer based on the Ruby style guide that reports potential issues in your code and will also attempt to fix some of them.
- Reek (SAST) – Doesn’t claim to lint code, but instead report any potential ‘code smells‘ that may cause harm to your code.
- Brakeman (SAST) – Specifically aimed at analyzing potential security issues in your Ruby on Rails code, including the vulnerabilities mentioned in this post, and more. You can run it periodically over your code base with
brakeman
command, or even better, hook it up to continuous integration for code commits. - Arachni and OWASP Zed Attack Proxy (DAST) – Both are entire frameworks that help penetration testers and security administrators investigate the security of web applications. Arachni has a Ruby library, but neither are limited to Ruby applications.
Ruby IDEs such as RubyMine, Aptana, and text editors such as Atom and Sublime Text typically have these tools available as plugins.
SQL Injection: Ruby and Rails Aren’t Bulletproof, But You Can Improve Your Odds
Thankfully for developers, the dark days of major Ruby security issues are behind us. As long as you follow the best practices touched on in this post, you can sleep soundly at night. Of course, with the more obvious issues fixed, hackers will be looking harder for more subtle ways into your application. Keep yourselves up to date with new vulnerabilities by using the services noted above, reading the Rails security page and the Rails security mailing list.
You can also actively protect your application by using Sqreen by just adding a small gem to your application. It will notify you of any vulnerabilities as they happen. Better yet: it will block most common attacks, giving you time to fix them.