Who stole the cookie from the cookie jar? (RoR Security)

When it comes to building out our own applications for the real world, it's good to keep in mind the idea of security. Sure, when you're working on some labs and executing code for school or practice, it may not be top of mind, however, hacking, security, and encryption are a big topic when it comes to the vulnerability of applications and user data.

When building out an app, our frameworks help us structure our application so we can build robust, functioning apps. Frameworks can also help us build a more secure app, but it's a good idea to know that one framework isn't more secure than another. When building out secure apps with Ruby on Rails, by default, it has some great security measures built in, but purely relying on those for security is not advisable. They do, however, have some clever methods developers can use to beef up security.

I won't be going into huge detail about advanced methods on how to make an app more secure, but I wanted to touch on some basic security measures you can implement on your app.

Table of Contents

What Ruby on Rails has to Offer for Basic Security

If you didn't already know, Ruby on Rails gained an overnight upsurge in popularity thanks to Apple, but in the beginning much of it's criticism lied around security after the 2012 breaches.

That being said, RoR devs since then have worked tirelessly to introduce a number of useful security updates to make sure the out-of-the-box security measures are are fool-proof as can be. Ruby on Rails now has an annual security audit once a year that improves security of the platform as a whole, but also helps mitigate a number of other difficulties. As of today, Ruby on Rails features an inbuilt default-protection against various types of security attacks. The development environment has graduated to become one of the safest development environment available today.

What makes the statement above true?

  • Thousands of people work on Ruby on Rails every day
  • Hundreds of people try to breach Ruby on Rails every day
  • Maintainers implement a strict security policy
    • Reported vulnerability is discussed via private channels
    • If confirmed, all the code is checked for similar vulnerabilities
    • Fixes cover all the supported releases, not only the latest one
    • Interested parties get notified along with the launch
    • They wait an additional 6 hours before the announcement

The best way to receive all the security announcements is to subscribe to the Rails Security mailing list. The mailing list is very low traffic, and it receives the public notifications the moment the embargo is lifted.

-Ruby on Rails "Security Policy"

Ruby on Rails Security HTTP Headers

So if you didn't already know, HTTP security headers, are a subset of HTTP headers which are exchanged between the web client (usually a browser) and a server. The security headers in particular specify the security related details of the HTTP communication. (If you don't understand the relationship between HTTP headers and the server, I would start with "How the Internet Works" before thinking about security.)

By default, RoR equips each request with a set of headers. These settings belong to the underlying ActionDispatch module, a huge part of Rails core, and responsible for routing requests to controllers.
lib/action_dispatch/railtie.rb

config.action_dispatch.default_headers = {
  "X-Frame-Options" => "SAMEORIGIN",
  "X-XSS-Protection" => "1; mode=block",
  "X-Content-Type-Options" => "nosniff",
  "X-Download-Options" => "noopen",
  "X-Permitted-Cross-Domain-Policies" => "none",
  "Referrer-Policy" => "strict-origin-when-cross-origin"
}

These are the provided default settings to ensure some security, and in most cases it doesn't need tampering with.

If you do need to change them, you can generally do that in these places:

  • config/application.rb
  • Dedicated environment configuration files

Let's quickly look at what each of these mean so you can understand what security measures RoR is implementing by default.

Security Header: X-Fram-Options

"X-Frame-Options" => "SAMEORIGIN",

The X-Frame-Options header with a secure SAMEORGIN value tells the browser that it should only open URL addresses linking to the same domain in the <iframe /> tags.

This is great for security, but it does not allow for parts of your app to be available in an iframe on a different domain.

You can override the value of X-Frame-Options globally using the config.action_dispatch.default_headers setting:

config.action_dispatch.default_headers['X-Frame-Options'] = "ALLOW-FROM https://apps.facebook.com"

Please look at additional documentation on how to change your x-frame-options for a specific controller or your whole app, as well as additional security you'll have to edit.

Security Header: X-XSS-Protection

"X-XSS-Protection" => "1; mode=block",

The X-XSS-Protection with value 1;mode=block enables the built-in Cross-Site Scripting (a type of attack) filter.

The first part, 1, simply turns the option on.

The second part, mode=block, prevents browsers from rendering pages if a potential XSS reflection attack is detected.

We'll discuss the basis of some Cross-Site Request Forgery later in this blog.

Security Header: X-Content-Type-Options

"X-Content-Type-Options" => "nosniff",

The X-Content-Type-Options with value nosniff is responsible for blocking a request if its destination is of either style or script types and their MIME types do not match.

MIME stands for "multipurpose internet mail extension" is an internet standard that describes the contents of internet files based on natures and formats. MIME types contain two parts: a type and sub-type.
Example MIME types:
text/html
application/json

Security Header: X-Download-Options

"X-Download-Options" => "noopen",

X-Download-Options is a header specific to Internet Explorer 8. Its functionality is to block the browser from executing the downloaded HTML file in the context of the website.

Security Header: X-Permitted-Cross-Domain-Policies

"X-Permitted-Cross-Domain-Policies" => "none",

X-Permitted-Cross-Domain-Policies with value none instructs clients such as AdobeAcrobat and Flash to avoid accessing any data from your domain.

Security Header: Referrer-Policy

"Referrer-Policy" => "none",

Referrer-Policy is responsible for controlling how much information should be sent in the Referrer header. This attribute is used to specify the reference information that will be sent to the server when the user clicks on a hyperlink. The strict-origin-when-cross-origin value:

  • Allows sending origin, path and query string for the same origin requests
  • Allows sending only the origin performing the cross requests, as long as protocol security stays the same
  • Does not send referrer header to less-secure destinations

In addition to some of the default measures, there are a few additional security headers we can look at that could be of value!

Addtional Security Headers

Security Header: Content-Security-Policy

Thanks to the hardworking RoR devs, there are some good sane default security headers for us, but there is another that we should look at called Content-Security-Policy. We can thank this header so we don't get hackers loading external scripts.

Content Security Policy (CSP) is an HTTP response header that restricts the browser to loading external assets such as scripts, styles or media from a wide variety of sources — as well as inline scripts.

- sqreen.com

You can take a look at some of these configurations below. Before making edits, make sure to read the proper documentation and implement proper gems and security when you're changing content-security-policy.

config/initializers/content_security_policy.rb

Rails.application.config.content_security_policy do |policy|
  policy.default_src :self, :https
  policy.font_src    :self, :https, :data
  policy.img_src     :self, :https, :data
  policy.object_src  :none
  policy.script_src  :self, :https
  policy.style_src   :self, :https
  policy.report_uri '/csp-violated'
end

Rails.application.config.content_security_policy_report_only = false

Security Header: Feature-Policy (experimental)

Feature-Policy header is another security header that Ruby on Rails let us configure, despite still being in the experimental state. RoR does not have documentation on this yet on their official guidelines, because of it's experimental state.

Be cautious of implementing a Feature-Policy on your app or website as things are subject to change at any time due to it's experimental state.

The code in the ActionDispatch module is very similar to the CSP one, and the header can be configured in the same manner.

Rails.application.config.feature_policy do |policy|
  policy.fullscreen :fullscreen
  policy.geolocation :geolocation
  policy.gyroscope :gyroscope
end

Ruby on Rails Common Security Attacks

In general, as noted before, Ruby on Rails comes bundled with well-balanced appliance and safety. The built-in Rails secure password and solutions allow a basic and secure level of protection against a variety of different attacks.

In the basis of the Ruby on Rails framework is a system of modules called gems. Each gem contains the code and metafile in the appropriate format (YAML - "Yet Another Markup Language).

A Remote Code Execution Attack can lead to a full-scale attack that would compromise an entire web application and the webserver. You should also note that virtually all programming languages have different code evaluation functions. So if a familiar RCE-exploit for YAML is inserted into the metafile and that gem is then loaded to the RubyGems server, it will allow you (the hacker) to execute any code in the context of the main Ruby code repository, thus bringing down the entire “ecosystem.”

Here's a basic model of how an RCE-exploit would work:

Essentially, after gaining access, the attacker might try to escalate privileges. This can completely compromise a vulnerable system.

We'll go into some of the most common types of security attacks of Ruby on Rails that development projects face.

XXS/Cross-Site Scripting

This is one of the most widespread security breaches on Ruby on Rails projects and it can ruin a web service in its entirety. It chooses from the numerous entry points to inject malicious codes into the project. A cross-site scripting attack can be launched from search result pages, messages, comments, reviews, etc. From here, the modified and often maligned item stays integrated into the app product and is accessible to a user.

Very often the malicious items stay passive for long durations in various parts of a website. This makes the structure of this particular security attack complex. It is often advised not to rely on standard XSS filters to prevent XSS attacks. If a programmer adds data in an unsafe format, such as JSON they enhance the risk. It is recommended to always convert the data to another format or avoid embedding of scripts into the transmitted data.

You should also think about implementing some kind of automatic screening of potentially dangerous components to protect from XXS breaches in RoR projects. This is made possible by marking every line with a special flag html_safe. In a case where such a flag is not set, Rails filters it before the output of the variable part.

Cross-Site Request Forgery

An abbreviation for cross-site request forgery, CSRFs are found on the vulnerability of the HTTP transfer protocol. Not only does it deter the performance and work of your app or web resource, but it also functions on assumptions of already active user privileges.

Here's how an attack may happen:

  1. Hacker links to your application on his website, by for ample placing the malicious link in the image file. <img src="http://myrails.com/resource/1/destroy" height=0 width=0 />
  2. Hacker's website visitor executes the code that otherwise requires authorization - as long as his session on myrails.com didn't expire.

However, you should note that RoR has an out-of-the-box security measure against this if you follow it's conventions.

With this ready-made mechanism called token authentication nothing gets executed. Those CSRF tokens are sent from the frontend to the backend layer with every form submission. If they don't match the expected ones's the request fails. Simple, yet effective.

SQL Injection

This is often described as a hacker's favorite, SQL injection is often used by perpetrators to find a way to pass unverified data. Not only does an SQL injection opens access to the database but it also provides an opportunity window to mess with confidential data by changing it. Hackers often use SQL Injection to look for certain information, as it allows looking for the required records quickly. They also enjoy the liberty to inject malicious code into the records.

The main consequences:

  • Confidentiality
  • Authentication
  • Authorization
  • Integrity

Clickjacking

A network attack that automatically redirects a user to another page without doing any harm to your site; clickjacking is lesser of evil. Hackers often use clickjacking attacks to increase the visitors of a third-party resource.

RoR development environment introduced a mechanism that can prevent redirects. This can be done by adding the HTTP header “X-Frame-Options: SAMEORIGIN” to the pages created.

Popular Security Gems for Ruby on Rails

Rails offers many out-of-the-box security measures as we have mentioned to protect against input validation flaws and other web-based attacks, but you have to understand that these mechanisms have a limit. That being said, it's best to implement diverse security measures to prevent against more malicious hacking through Rails gems. I'm just going to highlight some of the industry favorites.

Keeping all of the security vulnerabilities and measures constantly in mind is not an easy task to do, and depending on programmers memory alone is yet another security vulnerability, known as The Human Error.

Thankfully, there are measures to prevent it. One such way is automated audits for scanning your app for security flaws.

The most popular community choice for auditing Ruby on Rails application against security vulnerabilities is the Brakeman gem.

It is a popular authentication solution for applications in Rails. It provides a number of features such as offering secure password storage using bcrypt to hash salted passwords, user registration, and forgotten password functionality.

This was developed by the Twitter security team. It is a gem that implements security related HTTP headers into your application’s HTTP responses. secure_headers by Twitter includes security headers, such as a content security policy, to protect against cross-site request forgery, and HSTPS to restrict a browser from communicating with a remote server via https only.

Practical Security Best Practices with Ruby on Rails

Sessions

Session hijacking happens when a malicious user steals a legitimate user’s session ID in order to log in to a web application via the victim’s name. This form of attack is possible whether or not a user connects or makes an HTTP request to a remote web server via HTTP or HTTPS. (Information can also be accessed through Cross-Site Scripting) The only difference between HTTP and HTTPS is that an HTTPS connection has extra setup at the beginning. It negotiates a secure channel, and then it sends normal HTTP over that channel.

--

Quick side-bar on HTTPS configuration. Because the state of the internet, you should for good reason, implement the secure HTTP protocol (HTTPS) on your application. You can do this by obtaining a SSL Certificate completely free through providers such as Let's Encrypt.

Once you do that, setting up the Ruby on Rails to use the secure HTTPS protocol is as easy as it gets.
config/environments/production.rb

Rails.application.configure do
  # other config
  config.force_ssl = true
end

With this single line of code in place, you get:

  • Cookies flagged as secure
  • HTTP Strict Transport Security (HSTS) header that instructs the browser to perform subsequent requests via HTTPS only
  • Redirects all requests to the HTTPS

--

When a user logs in to a web application, a user ID is saved for future authentication in the session token. It looks like this:

session [:  user_id]  = user

The attacker can steal this cookie to log in to a web application via the user’s name. Attackers can hijack sessions in diverse ways such as predicting the session token, man-in-the-middle attack, or the infamous XSS.

Prevention

We can protect our cookies by using a random value in them. Let’s say attackers log in to a web application, they can easily predict the next or previous cookies by observing previous IDs. Thus, randomness of IDs is a must.

Moreover, web developers should not store IDs in the following way:

cookies[:secure_session]

Rather, web developers should call the signed method on cookies to encrypt the value like this:

cookies.signed[: secure_session]

In spite of unpredictable session IDs, we need to secure the way the client makes HTTP requests to remote web servers. Without SSL, cookies can be intercepted in transit. In Rails, there are a few ways to implement SSL.
(Mentioned in sidebar)

Cross-Site Request Forgery

A malicious user who happens to be a tutorial writer has set up a CSRF attack on cyber54.com to transfer a huge amount of money from a user’s account number. The malicious code may look like this:

<iframe src="http://examplebank.com/app/transfermoney? amount=2200&attackersAccount">

When the user uploads that iframe, the browser makes a POST request to www.mybank.com It will be processed and the amount of money would be transferred to account no: 247890345.

Prevention

In Rails we can prevent CSRF by authenticity_token in HTML responses. This token is also stored within the user’s session cookie. Forms generated in Rails may contain the following code:

<input
 Name= "authenticity_token"
        type= “hidden” 
 value=”ghtyu7asdvnTojibBNYY67BshjyerUA+81
+ DD=/>

The value of authentication_token differs. When the form and authentication_token are submitted, Rails verify the request to decide whether the request should be processed or not.

SQL Injection

Let’s say an attacker wants to perform a SQL injection on www.example.com. The attacker might check whether the site is vulnerable to SQL injection by the following code:
http://www.example.com/index.php?id=2'
When the remote web server serves an error page or message suggesting there is an error in our SQL syntax, then example.com is probably vulnerable to SQL injection.

Prevention

In Rails, applications interact with a database through ActiveRecord, an object-relational mapping (ORM) which by default comes with Rails. Although ORM provides database abstraction, careless handling of input can lead to SQL injection.

You should implement additional security measures via gems to heighten security for data such as usernames and passwords.

Generally a good solution is to sanitize your rendered data via ActionView::Helpers::SanitizeHelper module.

Conclusion - Securing the Cookie Jar

Even though Ruby on Rails is seen as a more secure framework it's gotten a lot of skepticism due to it's security breaches in the past. In my opinion, the RoR core team is constantly updating it's security measures and working hard to ensure a high level of base-security. If you go into RoR thinking it's base level of security is enough, it's similar to thinking if you have a lock on your door it'll be enough to prevent robbers. If you implement the many tools and guides Ruby on Rails developers suggest, you are sure to build a pretty secure and robust application using this framework. That being said there's a simple checklist you can go through when thinking about your app security.

Security Checklist

  1. Always check unauthorized access.
  2. Use authentication practices.
  3. Make it a point to filter passwords and other sensitive data logs.
  4. Use strong parameters to whitelist the values that can be used.
  5. Fix the number of throttling requests per minute.
  6. Use HTTPs for pages that deal with sensitive information.
  7. Use tools like a static analysis security vulnerability scanner for Rails applications.

21