The Inadequate Guide to Rails Security

Learn how to keep your Rails apps secure. If you try to follow best practices for security but don't really understand them, or are unclear about how XSRF, MITM, and XSR attacks work...then this guide is for you.

If you're like me, you got into this business because you love building awesome apps.

Let's take a second to reflect on the good times.

chunky-bacon

...back to the real world.

It sucks that you even have to worry about security. It's just sick that there are people who wake up every morning and try to smash the things you're building.

But they do...And when they succeed, it's your fault.

Your client can't make your code secure. Your manager can't. It's all on you. So you better know what the hell you're doing.

Should you read this?

  • Do you try to follow best practices for security but don't really understand them?
  • Are you unclear about how XSRF, MITM, and XSR attacks work?
  • Do you think that because your apps are small, or internal, they're not at risk?

...then this guide is for you.

You're being attacked, now! Freak out!

If it's on the internet, it's under siege. Automated systems are scanning around the clock for vulnerable web apps.

By the mafia

Are you storing credit cards in your database? Unsalted password hashes? You're screwed.

By bot-masters

defense_intelligence_mariposa_botnet_map

When you run a botnet, more bots equals more money.

  • So you buy an exploit pack!
  • Set it up to install your malware when an old browser hits the payload
  • Hack some legit sites to deliver the payload to lots of users.

By governments

It sounds like a joke. But the Chinese government has thousands of people hacking systems in search of:

  • Trade Secrets
  • Government Intelligence
  • Information about political enemies.

They were recently caught on tape.

And, umm..camera. Doesn't that look like a fun job?

hackers

By Hacktivists

Do you sell depleted uranium slugs to the DOD? Then you should have enough money to hire someone who knows what they're doing.

Required reading

This is the inadequate guide. Did you expect it to be comprehensive? At minimum you should also read:

Eight easy ways to get p0wn'd

We're going to cover eight classes of vulnerabilities that are crucial for web developers to understand.

  • Non Technical
  • Cross Site Scripting (XSS)
  • Cross Site Request Forgery (CSRF, XSRF)
  • Man in the Middle (MITM)
  • SQL Injection (SQLI)
  • Mass Assignment & Parameter Injection
  • Denial of Service (DOS, DDOS)
  • Platform Attacks (Hacking linux boxen)

Non-Technical Exploits

Sometimes the obvious problems are the hardest to see.

Security Comic

The number and variety of stupid things you can do is endless, so we'll just point out a few.

Don't:

  • Send plaintext passwords via email
  • Use the same password everywhere
  • Trust people who show up at your office with cookies
  • Carry sensitive data on your laptop

Cross Site Scripting

If I'm an attacker, I'd be very interested in ways to insert some of my JS into your page.

Once I do that I can:

  • Steal session cookies
  • Bootstrap an exploit kit into the dom
  • Make the pages say wacky things

vote_hillary

There's a simple defense

To thwart me, you need to escape any text that a user has entered, so javascript gets turned into harmless text.

You want this

<script>alert('I am stealing your cookies! Ha!');</script>

not this:

<script>alert('I am stealing your cookies! Ha!');</script>

Except when there's not

Here's the thing. If you have an old rails 2 application, you probably have XSS vulnerabilities. That's because rails 2 made you manually escape untrusted input, using the h() method.

Rushing to meet a deadline? Pushing a quick bugfix? Outsourcing work to a junior developer? The h() would be forgotten. Kittens would cry.

Rails 3 and 4 are much better. Output is escaped by default. But it's still possible to shoot yourself in the foot.

Cross site request forgery

If a user wants to change their password, they post a form, right? So what happens if the form isn't on your website?

The request still goes through. Attackers can exploit this fact.

Anatomy of a CSRF attack

  • I'm an attacker
  • I know you're logged in to your app as an admin
  • I create a "change password" form disguised as something else
  • You submit the form and, without realizing it, you've changed your password.

And it's even worse if you're using GET requests instead of POSTS.

I wouldn't even have to trick you. I could cause an authenticated request to happen just by adding an image tag to a page you visited.

Rails saves your bacon

Luckily, rails sidesteps this class of attack by offering CSRF protection. You may know it as "that weird text that gets put in all my forms for some reason."

The weird text is a secret key that is stored in the user's session. If the user submits a CSRF token that doesn't match the one in the session, you get an exception.

But you can still screw it up

Of course you can disable CSRF checking like so:

skip_before_filter :verify_authenticity_token

And you can add GET routes for things that should be POSTS:

MyApp::Application.routes.draw do
  match "/launch_all_the_missiles", to: "missiles#launch_all"
end

But you wouldn't do that, would you?

If you're not using rails

And keep in mind that if you're using a minimalist framework like sinatra or goliath, you'll need to handle this yourself. Here's an example.

Man In The Middle / Packet Sniffing

So you need to send some cash to your rotten no-good cousin Vinny. You take the cash, put it in an envelope, write that bastard's address, add a stamp and hand it to the mail man.

A week later, the envelope arrives with no cash. This is a simple man in the middle attack.

The same thing can happen with your user's cookies.

Cookies are usually sent with every request. If they're sent in cleartext over a public (wifi) network, game over.

An evil-doer running FireSheep can easily get copies of the user's cookies and compromise their account.

SSL to the rescue

The easy answer is to use HTTPS for everything. The people in the cafe can still see the user's traffic, but it's encrypted.

A not-so-easy-but-perhaps-better-in-some-circumstances answer is to use rails' secure cookies.

Secure cookies will only be sent to the browser over an HTTPS connection.

...that is if you're running rails >= 2.3.10 or >= 3.0.2 .

Before that, secure cookies could be sent over plain text. Don't ask.

SQL Injection

99% of all websites work by glueing fragments of text together to make little programs in a language called SQL.

Just think about that. Do you have any idea how backasswards this is?

But like it or not, this is the way things work. And based on the fact that the internet exists, it must work reasonably well.

But one side effect of this is that if a hacker can add their own bits of text into little SQL programs we are absolutely screwed.

exploits of a mom

Example

Imagine that your contractor sends you some code that looks like this.

Building.where("st_intersects(st_setsrid(st_makebox2d(st_point(#{params[:northeast][:lng]}, #{params[:northeast][:lat]}), st_point(#{params[:southwest][:lng]}, %{params[:southwest][:lat]})), 4326), buildings.location)")

This is a huge mistake. I can make the params[:southwest][:lat] contain

")), 4326), buildings.location); UPDATE users SET admin=1 WHERE id=666;--"

And now I have admin privileges.

Rails protects us...

Rails protects in some cases. If we'd used where correctly, the parameters would have been excaped, and SQL injection attempts would fail.

where("st_intersects(st_setsrid(st_makebox2d(st_point(?, ?), st_point(?, ?)), 4326), #{table_name}.location)", box[:northeast][:lng], box[:northeast][:lat], box[:southwest][:lng], box[:southwest][:lat]).

...until it doesn't

But there are lot of places where rails uses raw sql. Places you might easily overlook.

For example, did you know that in SomeModel.sum("amount"), the "amount" string is added to the query without being escaped?

So if you have a report where you sum parama["col"], you're in trouble.

Here's an example IRB session, showing the consequences of passing untrusted input into the sum method:

pry(main)> User.sum(%[id) AS sum_id FROM users; update users set
admin='t' where id=224;--])
(22.4ms)  SELECT SUM(id) AS sum_id FROM users; update users set
admin='t' where id=224;--) AS sum_id FROM "users" 
=> "0"

The same is true for #pluck, and a host of other methods.

For more detailed info, check out: http://rails-sqli.org/

Mass Assignment Attacks

March 4, 2012, Russian hacker Egor Homakov disclosed a mass assignment vulnerability that could let an attacker masquerade as any github user.

How github was hacked

I bet you've written code like this hundreds of times:

User.create(params[:user])

This of course creates a user with the given params.

Prior to Rails 3.2.3, that meant that any parameter that was input would be assigned to the model...unless you explicitly specified otherwise.

Problem is, this was easy to forget. And so you find yourself in a world where an attacker just has to send in the right POST request to cause this to happen:

User.create({admin: true, ...})

Or in github's case, something like this:

PublicKey.update({user_id: "123"})

It's an easy fix

Of course you avoid these problems by using attr_protected, or attr_accessible

class PublicKey < ActiveRecord::Base
  attr_protected :user_id
end

But this is easy to overlook.

Therefore, if you have an app running rails < 3.2.3, it's recommended that you make all attributes protected by default. Then you're forced to explicitly list the ones you want to be whitelisted.

This article shows you how: https://gist.github.com/petenixey/1978249

Rails brings down the hammer

Rails 3.2.3 comes like this out of the box. And Rails 4 will use an altogether different whitelist mechanism, called Strong Parameters. It's available as a plugin if you can't wait: https://github.com/rails/strong_parameters

Denial of Service

How many people have you pissed off today? Last month?

DOS, and DDOS attacks are infosec's version of a rage comic. They're not going to steal your data or spread malware. They just want to shut you down.

DDOS is crude but effective

To mount a destributed denial of service attack, just call up 500 of your closest friends, have them head over to amnesty international's website and keep pressing refresh for an hour. Bots can be substituted for friends.

If you have problems like these, talk to your ISP, use cloudflare, etc. They're pretty boring and well known problems.

Algorithmic Complexity Attacks

That just sounds freaking cool, doesn't it?

These take advantage of a particular weakness in your operating system, application stack, etc. to fill up your server's memory, peg your CPU, etc.

For example, the classic SYN flood attack works by sending SYN requests to a server. The server opens a ton of connections which aren't closed. With enough open connections, the OS will run out of ram.

A recent exploit

Ancient history? Not really. A recently patched flaw in the JSON gem would let an attacker max out your ram by creating millions of Ruby symbols. Symbols are never garbage-collected.

Good old fashioned server exploits

This is really what we think of when we think of hackers, right? People using buffer overflows to cause apache to run arbitrary code as root.

But like most boring jobs, this too has been automated. If you look at your system logs right now, I bet you'll see bots trying to brute force an SSH login.

Automate attack detection

You can use something like fail2ban to automatically block the IP addresses of hosts that are attempting known attacks against you.

Do the things you know you should do.

Keep your OS Patched. Don't run services you don't need. Don't allow password authentication for SSH. The list goes on.

Conclusion

PANIC!

Then start taking security seriously. This is part of your job whether you like it or not.

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo

    Starr Horne

    Starr Horne is a Rubyist and Chief JavaScripter at Honeybadger.io. When she's not neck-deep in other people's bugs, she enjoys making furniture with traditional hand-tools, reading history and brewing beer in her garage in Seattle.

    More articles by Starr Horne
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial
    Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
    Start free trial
    “Wow — Customers are blown away that I email them so quickly after an error.”
    Chris Patton, Founder of Punchpass.com
    Start free trial