4 Essential Security Tools To Level Up Your Rails Security
Tags: rails, security
Published: 2023-05-31 20:15:24

---
title: 4 Essential Security Tools To Level Up Your Rails Security
published: true
date: 2023-05-31 11:23:45 UTC
tags: rails,security
canonical_url: https://fastruby.io/blog/rails/security/ruby-security-toolkit.html
---

At FastRuby.io we love [Ruby on Rails](https://rubyonrails.org) because it is so powerful: You can quickly create an application that is feature complete, stable, and [secure](https://guides.rubyonrails.org/security.html)

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.

<!--more-->

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:

```ruby
# 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](https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf). 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:

```ruby
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`](https://github.com/cyu/rack-cors) gem:

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

```

You can configure it as a middleware like this:

```ruby
# 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:

```ruby
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](#bundler-audit)
- [Brakeman](#brakeman)
- [Rack::Attack](#rack-attack)
- [Secure Headers](#secure-headers)

## Bundler Audit

This Ruby gem is quite useful for detecting versions of gems that are known to be vulnerable to security issues. [`bundler-audit`](https://github.com/rubysec/bundler-audit) uses an open database of vulnerable gems called [`ruby-advisory-db`](https://github.com/rubysec/ruby-advisory-db) and compares it to the versions that show up in your `Gemfile.lock`.

You can install it and run it like this:

```bash
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](https://fastruby.io/blog/assets/images/bundle-audit-output-screenshot.png)

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

![bundle-audit output for an app that has no vulnerable dependencies](https://fastruby.io/blog/assets/images/bundle-audit-no-vulnerabilities.png)

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](https://audit.fastruby.io)

It usually looks like this:

![bundle-audit results in the open source application hosted by FastRuby.io](https://fastruby.io/blog/assets/images/audit-fastruby-io-screenshot.png)

`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`](https://brakemanscanner.org/) 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](https://github.com/presidentbeef/brakeman/tree/main/lib/brakeman/checks)

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

- High: I’m using Rails 6.1 with Ruby 2.7.2. [Security support for Ruby 2.7.x ended on March 31st, 2023](https://twitter.com/yukihiro_matz/status/1639758019235053571).

- Weak: I’m using a version of [`rails-html-sanitizer`](https://rubygems.org/gems/rails-html-sanitizer) that is known to be vulnerable.

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](https://www.fastruby.io/blog/ruby/upgrades/upgrade-ruby-from-2.7-to-3.0.html).

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:

```ruby
# 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:

```ruby
# 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:

```ruby
# 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](https://www.kaspersky.com/resource-center/definitions/brute-force-attack):

```ruby
# 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:

```ruby
# 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](https://www.cloudflare.com) as a good way to prevent [DDoS attacks](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/). 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`](https://github.com/github/secure_headers) 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:

- [OWASP’s Ruby on Rails Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html)
- [Ruby on Rails Security Email List](https://groups.google.com/g/rubyonrails-security)
- [Brakeman](https://github.com/presidentbeef/brakeman)
- [Ruby Security Email List](https://groups.google.com/g/ruby-security-ann)
- [Rack::Attack](https://github.com/rack/rack-attack)
- [Bundler::Audit](https://github.com/rubysec/bundler-audit)
- [Ultimate Guide to Rack::Attack](https://wafris.org/guides/ultimate-guide-to-rack-attack)
- [Awesome Ruby Security Resources](https://github.com/pxlpnk/awesome-ruby-security)

## 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!](https://fastruby.io)