4 Essential Security Tools To Level Up Your Rails Security

4 Essential Security Tools To Level Up Your Rails Security

At FastRuby.io we love Ruby on Rails opens a new window because it is so powerful: You can quickly create an application that is feature complete, stable, and secure opens a new window

Unfortunately, maintaining a Rails application up to date and secure takes some effort.

In this blog post, we will cover a few Ruby gems and best practices that you can use to stay on top of your security, reliability, and stability needs.

Before we dive into 4 different Ruby gems you can use to improve your security support, let’s start with some of the security goodies that already come with Rails.

Rails’ Native Security Features

The good news is that Rails comes with a ton of security support, here are a few features that you are probably already using in your application:

Forcing SSL

You can easily secure your authentication by enabling TLS in your configuration:

# config/environments/production.rb
# Force all access to the app over SSL,
# use Strict-Transport-Security,
# and use secure cookies
config.force_ssl = true

If your web server (e.g. nginx) doesn’t force SSL, your Rails application will.

CSRF (Cross Site Request Forgery)

Some of these security features are enabled by default. For instance: CSRF opens a new window . Ruby on Rails has specific, built-in support for CSRF tokens.

If you see this line in your application_controller.rb, you know that you are including Rails’ protection against cross site request forgery:

class ApplicationController < ActionController::Base
  protect_from_forgery
  # ...

CORS (Cross Origin Resource Sharing)

Occasionally, a need arises to share resources with another domain. For example, a file-upload function that sends data via an AJAX request to another domain.

In these cases, the same-origin rules followed by web browsers must be sent. Modern browsers, in compliance with HTML5 standards, will allow this to occur but in order to do this; some precaution must be taken.

For this, you can use the rack-cors opens a new window gem:

gem 'rack-cors', :require => 'rack/cors'

You can configure it as a middleware like this:

# config/application.rb:

module Sample
  class Application < Rails::Application
    config.middleware.use Rack::Cors do
      allow do
        origins 'audit.fastruby.io'
        resource %r{/users/\d+.json},
        :headers => ['Origin', 'Accept', 'Content-Type'],
        :methods => [:post, :get]
      end
    end
  end
end

Filtering Parameters

To make sure sensitive request parameters aren’t logged to your log files, you can set up your application like this:

Rails.application.config.filter_parameters += [
  :credit_card_number,
  :password,
  :social_security_number
]

Values for these parameters will now show up as "[FILTERED]" in your log files.

Now that we have covered some of the key security features in Rails, let’s look at 4 gems that can help you level up your security best practices:

Bundler Audit

This Ruby gem is quite useful for detecting versions of gems that are known to be vulnerable to security issues. bundler-audit opens a new window uses an open database of vulnerable gems called ruby-advisory-db opens a new window and compares it to the versions that show up in your Gemfile.lock.

You can install it and run it like this:

gem install bundler-audit
bundle-audit

If there are vulnerable versions in your Gemfile.lock it will show you something like this:

bundle-audit output for an app that has vulnerable dependencies

If everything looks good, it will show you something like this:

bundle-audit output for an app that has no vulnerable dependencies

A few years ago we built a small, open source, web application to make it easier for us to share these vulnerabilities reports with our clients. You can find it and use it over here:

Bundler Audit Tool opens a new window

It usually looks like this:

bundle-audit results in the open source application hosted by FastRuby.io

bundler-audit relies on ruby-advisory-db, a community-maintained database of known security vulnerabilities. While this is a great approach, it may also lead to gaps in coverage if a vulnerability is not yet documented in the database or if the database becomes outdated.

If you want to make sure that bundler-audit is running with the latest version of the ruby-advisory-db, you should always call it like this:

bundle-audit check --update

Brakeman

brakeman opens a new window is another useful Ruby gem that is a static analysis security vulnerability scanner for Ruby on Rails applications.

You can quickly run it like this:

cd path/to/rails-app
gem install brakeman
brakeman

This will run a battery of “checks” against your source code and report any potential security issues:

Loading scanner...
Processing application in /Users/etagwerker/Projects/ombulabs/foobar
Processing gems...
[Notice] Detected Rails 6 application
Processing configuration...
[Notice] Escaping HTML by default
Parsing files...
Detecting file types...
Processing initializers...
Processing libs...
Processing routes...
Processing templates...
Processing data flow in templates...
Processing models...
Processing controllers...
Processing data flow in controllers...
Indexing call sites...
Running checks in parallel...
 - CheckBasicAuth
 ...
 - CheckYAMLParsing
Checks finished, collecting results...
Generating report...

== Brakeman Report ==

Application Path: /Users/etagwerker/Projects/ombulabs/foobar
Rails Version: 6.1.4
Brakeman Version: 5.4.1
Scan Date: 2023-05-02 15:18:55 -0400
Duration: 0.430032 seconds
Checks Run: BasicAuth, ..., YAMLParsing

== Overview ==

Controllers: 3
Models: 2
Templates: 15
Errors: 0
Security Warnings: 2

== Warning Types ==

Cross-Site Scripting: 1
Unmaintained Dependency: 1

== Warnings ==

Confidence: High
Category: Unmaintained Dependency
Check: EOLRuby
Message: Support for Ruby 2.7.2 ended on 2023-03-31
File: .ruby-version
Line: 1

Confidence: Weak
Category: Cross-Site Scripting
Check: SanitizeConfigCve
Message: rails-html-sanitizer 1.3.0 is vulnerable to cross-site scripting when `select` and `style` tags are allowed (CVE-2022-32209). Upgrade to 1.4.3 or newer
File: Gemfile.lock
Line: 194

To see a complete list of checks ran by Brakeman, you can find them over here: List of Brakeman Checks opens a new window

This sample application is in pretty good shape, but it does report two warnings:

One of them is quite easy to fix: bundle update rails-html-sanitizer and the warning goes away. We can safely deploy this to production with a small pull request.

The other warning might be harder to accomplish: We need to upgrade from Ruby 2.7 to 3.0 opens a new window .

Just like any other static analysis tool, brakeman may generate false positives or negatives. This means that it might report issues that aren’t actually problems or fail to detect actual vulnerabilities. We need to be cautious when interpreting the results and not rely solely on these tools for ensuring security.

Rack::Attack

rack-attack is a middleware that can be used in your Rails application (and any rack-based Ruby applications!) to protect it from bad clients.

Rack::Attack lets you easily decide when to allow, block and throttle based on properties of the request.

Here are some of its key features:

Safelisting / Blocklisting

You can quickly allow an IP like this:

# config/initializers/rack_attack.rb (for rails app)
Rack::Attack.safelist_ip("5.6.7.0/24")

This could be useful if you want to allowlist your development team’s IP addresses.

You could quickly block an IP like this:

# config/initializers/rack_attack.rb (for rails apps)
Rack::Attack.blocklist_ip("1.2.3.4")

It’s a powerful combination to combine rack-attack with environment variables like this:

# config/initializers/rack-attack.rb

bad_ips = ENV["BLOCKLIST_IPS"].split(",") # to be blocked

Rack::Attack.blocklist "blocklist bad IP address" do |req|
  bad_ips.include?(req.ip)
end

good_ips = ENV["SAFELIST_IPS"].split(",") # to be safelisted

Rack::Attack.safelist "safelisted good IP address" do |req|
  good_ips.include?(req.ip)
end

Throttling

This would be a good way to slow down an attacker who is trying to sign in to a user account using a brute force attack opens a new window :

# Throttle login attempts for a given email parameter to 6 reqs/minute
# Return the *normalized* email as a discriminator on POST /login requests
Rack::Attack.throttle('limit logins per email', limit: 6, period: 60) do |req|
  if req.path == '/login' && req.post?
    # Normalize the email, using the same logic as your authentication process, to
    # protect against rate limit bypasses.
    req.params['email'].to_s.downcase.gsub(/\s+/, "")
  end
end

Additionally, you could have a more broad criteria for limiting the number of requests per minute:

# config/initializers/rack_attack.rb (for rails apps)

Rack::Attack.throttle("requests by ip", limit: 5, period: 2) do |request|
  request.ip
end

Based on the IP of the user, this configuration would limit their rate to 5 requests every 2 seconds.

You can combine rack-attack and Cloudflare opens a new window as a good way to prevent DDoS attacks opens a new window . Just because you are using rack-attack, it doesn’t mean that you should not use a tool like Cloudflare.

Secure Headers

The secure_headers opens a new window gem will automatically apply several headers that are related to security. This includes:

  • Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. Default value: default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'

  • HTTP Strict Transport Security (HSTS) - Protects from SSLStrip/Firesheep attacks. Default value: max-age=631138519

  • X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. Default value: sameorigin

  • X-XSS-Protection - Cross site scripting heuristic filter for IE/Chrome. Default value: 1; mode=block

  • X-Content-Type-Options - Prevent content type sniffing. Default value: nosniff

  • X-Download-Options - Prevent file downloads opening. Default value: noopen

  • X-Permitted-Cross-Domain-Policies - Restrict Adobe Flash Player’s access to data. Default value: none

While secure_headers provides sensible safe default settings, it is essential to tailor them to your application’s specific needs.

Additional Resources

If you’re interested in reading more about securing your Rails applications, here is a list of useful resources:

Conclusion

While Rails provides a lot of security features, there are at least 4 ways to level up your application’s security with these great tools:

  1. bundler-audit to find dependencies that are known to have vulnerabilities
  2. brakeman to find idioms/calls that could be dangerous for your application
  3. rack-attack to defend your application against bad, abusive clients
  4. secure_headers to quickly apply several security headers to all your responses

This list is by no means exhaustive but it shows some of the key tools you can use to stay on top of potential security issues.

Although tools like bundler-audit and brakeman are helpful in identifying potential vulnerabilities, they should not be solely relied upon.

As a team, you should define a comprehensive security strategy which includes regular code audits, code reviews, and penetration testing.

Happy hacking!

PS: Need a code security audit? Contact us! opens a new window

Get the book