At FastRuby.io we love Ruby on Rails because it is so powerful: You can quickly create an application that is feature complete, stable, and secure
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. 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
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
uses an open database of vulnerable gems called 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:
gem install bundler-audit
bundle-audit
If there are vulnerable versions in your Gemfile.lock
it will show you something like this:
If everything looks good, it will show you something like this:
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:
It usually looks like this:
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
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
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.
Weak: I’m using a version of
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.
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:
# 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 as a good way to prevent DDoS attacks. 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
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
- Ruby on Rails Security Email List
- Brakeman
- Ruby Security Email List
- Rack::Attack
- Bundler::Audit
- Ultimate Guide to Rack::Attack
- Awesome Ruby Security 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:
-
bundler-audit
to find dependencies that are known to have vulnerabilities -
brakeman
to find idioms/calls that could be dangerous for your application -
rack-attack
to defend your application against bad, abusive clients -
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!
Top comments (0)