As your application gets larger and larger, the surface area for security issues expands accordingly, and security bugs become more and more problematic.
Here are a few tips to avoid some common pitfalls regarding security for Rails apps.
It is quite common to want to mix I18n translation keys with HTML tags. I’d recommend against doing that as much as possible, but sometimes you can’t really avoid it.
Let’s take the following example:
We have a problem here, because this will produce the following output:
Oops! Indeed, our string was never marked as html safe, therefore rails will escape html entities.
One (bad) way to fix it would be to do the following:
XSSes are often underrated as benign security issues, but they can be fatal if exploited properly.
Fortunately, Rails has a nice solution for us: if an I18n key ends up with
_html, it will automatically be marked as html safe while the key interpolations will be escaped!
Note that this is pretty much the same as doing this:
One good way to avoid XSSes is to really try to avoid using
raw) as much as possible, and when forced, double check that you have full control of the content displayed.
You can’t trust user params; you most likely already know that. But there are different ways to implement sanitization of user params.
Let’s pretend we have a form, and we want to use one of two different Form Objects depending on a param:
Here, we get the good form class by constantizing a string that is controllable by the user. This is very bad practice and can lead to terrible side effects (imagine sending
make_user_admin instead of
One solution could be to do this:
Here we are ‘safe’. We check that the params are one of the two expected values and only constantize if needed. While this works fine, we haven’t corrected the root security issue (which is the use of
constantize over user input).
Code grows old and evolves, developers copy and paste parts constantly, and at some point your offending line could end up outside of its guard.
Now let’s have a look at this alternative:
We have the same behaviour as above, except this time we don’t use
By being defensive and keeping a close eye on user input, we can avoid many basic security issues.
Consider the following code:
This code is voluntarily weird-looking in its structure to be vulnerable to the security issue, but trust me, I’ve seen it in the wild ;)
Everything works ok here until we start messing a bit with the params.
Let’s imagine we send the following request:
Rails will parse
params[:id] as an array:
[42, 43, 44, 45]
Thanks to some weirdness in
find_by and messing with the params, here we managed to act on records we may not have access to.
It’s always good to remember that params can also be arrays (or hashes!) as that can pose some security risks.
Most of us are very wary when dealing with user params, or values coming from the database.
However, there are some attack vectors that can be forgotten, including (but not limited to) the following:
It’s always good to think about where any given input comes from and wonder if it can be tampered.