Rails security – Dropping your code into shark-infested waters

| 6 min. (1073 words)

Let’s talk about Rails security! Even after you’ve crafted a killer web app with a feature for even the dingiest corners of the problem domain, sent the unit test coverage into the stratosphere, and set up a rock solid production infrastructure, having a live app can be fraught with danger. It’s like dropping the newborn puppy that is your code into shark-infested waters.

Attack vectors like cross-site scripting and request forgeries, violations of the same-origin policy, SQL injection, session hijacking and more are realities when releasing code that accepts web requests. The reality is that the modern web is full of bad actors, and developers of apps that store and process user data have a responsibility to harden their code against such attacks.

Fortunately, Rails gives you a lot of nice tools that handles some common vunerabilities easily, which we’ll look over below, in addition to a look at one of the cool bonus features of Raygun, which is how it gives you visibility on automated attack scripts crawling your site for secuity holes.

Session hijacking is a very real concern in Rails, due to the critical data stored in the session cookie. Many attacks focus on getting at this data, in order to nab credentials for nefarious purposes. A classic Man-in-the-Middle (MITM)attack is a real possibility over plaintext HTTP connections, especially with omnipresent public WiFi. Fortunately, there’s a one-liner to guard against this:

config.force_ssl

It’s almost a requirement that you support TLS if you’re going to be dealing with sensitive customer credit card data, comes highly recommended in general. The certification process is a bit of a pain, yes, but not compared to stolen customer data.

Cross-site scripting

Accepting user-provided text provided a common security hole which still must be considered when rendering that text to the DOM, as this can contain malicious JavaScript. This can be used to steal the user’s session cookie (or, more worryingly, call alert() with a naughty message).

Rails 3 and 4 are awesome and escape strings by default, replacing dodgy < and >’s with their htmlencoded equivalents, mitigating XSS attacks.

However, Rails 2 doesn’t – user-provided strings (like comments) must be wrapped in h().

Cross-site request forgery

CSRF attacks involve a third-party site linking to one of your actions with some params set. If the visitor to that third-party site is auth’d to your site, the action is executed without their permission. Can be highly dangerous when financial data is in play, and there’s been successful attacks against large sites in the past, so it’s always one to keep in mind.

The behavior for guarding against this changed from Rails 3 to 4. Rails 3 comes set up with this as default:

protect_from_forgery

When migrating to Rails 4, you’ve got two options, depending on whether you’re rendering with MVC or providing a raw API:

# MVC, throw an exception (see it in Raygun!)

protect_from_forgery with: :exception

# Null out the session object if a forged request is received, good for APIs
protect_from_forgery with: :null_session

SQL injection

Accepting and including user input in SQL queries can cause all manner of havoc, allowing an attacker to get in to your DB and get whatever they want.

ORMs like ActiveRecord provide some guards against common attack vectors, but it is still possible to execute methods that can open up vunerabilities. When materializing data from your DB with ActiveRecord, keeping this guy in your head is a good step.

See live hack attempts with Raygun

One of the cool surprises that Raygun gives you is visibility on attack scripts that are probing your site for security holes. We hear from our users at how often their sites are being crawled for vunerabilities.

Raygun shows the exceptions and HTTP error codes that are thrown from invalid request as these malicious bots attempt to access common vunerabilities (such as framework admin pages). This data would normally be lost in the logs, if caught at all (as these errors might be traditionally disabled on production). As these are caught, sent to Raygun and grouped automatically, you get a count of just how many are being attempted, and the IP address of the bot so it can be blacklisted.

Configuring Raygun4Ruby

It’s simple to integrate Raygun into your Rails app to get the above visibility. We have support for Ruby on Rails error handling, just grab the library:

gem install raygun4ruby

Then run this:

rails g raygun:install YOUR_API_KEY

The API key is unique to your app, and you can get it after creating a free Raygun trial account.

Secure Raygun configuration

The default configuration will automatically catch all exceptions in the Production environment, and TLS is enforced when exceptions are sent to the Raygun API. There’s also a few security-minded options you may want to turn on however.

You’ll find the configuration in config/initializers/raygun.rb. It contains the setup block, where you can add additional options:

Raygun.setup do |config|
  c1onfig.api_key = "YOUR_API_KEY"

  # Place additional config options here
end

Filtering parameters

If your users are posting sensitive data to your actions, and you don’t want these sent along with the exceptions to Raygun, you can filter these out in the setup block like this:

config.filter_parameters = [ :password, :card_number, :cv2 ]

That array should contain the keys that you want blacklisted. You can alternatively set a block that picks out the keys you want to send:

config.filter_parameters do |params|
    params.slice("safe_key", "insecure_data") # note that Hash#slice is in ActiveSupport
end

Proxy

Send the exceptions through a proxy is easy:

config.proxy_settings = { host: "localhost", port: 8888 }

Secure user tracking

Raygun4Ruby supports user tracking in order to see what users are affected by the exceptions your app raises. By default :current_user attempts to be called in order to get the current user ID. This can be customized to set an random ID like this:

#config.rb

config.affected_user_method = :raygun_user

# root controller

class ApplicationController < ActionController::Base

  def raygun_user
    cookies.permanent[:raygun_user_identifier] ||= SecureRandom.uuid
  end

end

Start your free Raygun trial now

If you’ve haven’t yet got a Raygun account, start your free trial now. If you’re (un)lucky you might even see one of the malicious crawlers hit your site for vunerabilities – we guarantee you’ll be stunned at what the crimeware syndicates have out there these days. Hopefully though you’ll just gain visibility on the bugs you normally miss in production, leading to a safe and secure app. Happy error blasting!