Clutch

Security issues solutions in RoR

Security issues solutions in RoR
Average rating: 3
(1 votes)

We have published many articles about Rails benefits and how good this framework for different types of marketplaces or web applications. But there’s nothing perfect in this world. So every single programming language or a framework has specific problems to be fixed by the developers. In this article, we’ll show you security issues solutions in RoR. 

Despite Ruby on Rails has many advantages, it is not possible that any framework will cover every possible scenario and there is always an application on top which is usually a source of the vulnerabilities. Every business wants to create secure web applications and websites, and as Ruby on Rails agency, we’d like to give a few tips how to prevent potential security problems.

Also, we would like to say thanks to Krzysztof Kempiński for his Preventing security issues in Ruby on Rails article we used for our material.

You may hear about OWASP (Open Web Application Security Project) organization that works on improving the security of software. In our article we will use the list of tips to improve Rails security gathered by OWASP.

So here are quick basic Ruby on Rails security tips for developers:

Command injection

Command injection is not exactly RoR related but more refers to Ruby. It’s about executing, either in Ruby or in OS a command that comes from outside world (like from the UI). Be careful using it in Rails based applications. And do never run any of these if an argument comes from the user and is not validated and filtered.

eval("ruby code") - running Ruby code
`ls -al` - running OS command
Kernel.exec("rm -rf") - running OS command

eval(“ruby code”) – running Ruby code
`ls -al` – running OS command
Kernel.exec(“rm -rf”) – running OS command

SQL Injection

Ruby on Rails uses ActiveRecord ORM for communication with the database. To build a query for some data we need some input from the user and this is a fragile place in the code. Imagine a piece of code like this:

Product.where("name like ‘" + params + "‘")

The search is glued to the SQL statement without any other actions on it so easily someone can do anything on our database. Prevention: use built-in ActiveRecord mechanisms for SQL injection prevention. And use caution not to build SQL statements based on user-controlled input. A list of more realistic and detailed examples is here: rails-sqli.org

Cross-site Scripting (XSS)

By default, in Rails 3.0 protection against XSS comes as the default behavior. When string data is shown in views, it is escaped prior to being sent back to the browser. This goes a long way, but there are common cases where developers bypass this protection – for example, to enable rich text editing. You can extract it using methods like rawcontent_tag or html_safe but these are all bad examples of what you should be doing. Don’t do like this:

<%= link_to "My Website", @user.website %>

Because if @user.website contains JavaScript code, someone can steal data from your browser.

Consider accepting input from the user as a markup language for rich text (markdown or textile). You can also use #sanitize method to filter-out any markup tags that you don’t want to have. Be careful, this method has been shown to be flawed numerous times and will never be a complete solution. OWASP provides more general information about XSS in a top-level page: Cross-site Scripting (XSS).

Sessions

By default, Ruby on Rails uses a Cookie based session store. What that means is that unless you change something, the session will not expire on the server. That means that some default applications may be vulnerable to replay attacks. It also means that sensitive information should never be put in the session.

The best practice is to use a database based session, which thankfully is very easy with Rails:

Project::Application.config.session_store :active_record_store

Store your sessions with the database backend.

Authentication

Rails does not provide authentication by itself. However, most developers using Rails leverage libraries such as Devise or AuthLogic to provide authentication. To avoid vulnerabilities use existing gems that proved to be good for this, like Devise. Also good practice will be to enhance security even more by for example changing validation for a length of password that is 6 characters for Devise to something like 10.

Insecure Direct Object Reference or Forceful Browsing

RESTful application design is very popular in Rails and is a good practice. That means that paths are often intuitive and guessable. To protect against a user trying to access or modify data that belongs to another user, it is important to specifically control actions. Out of the gate on a vanilla Rails application, there is no such built-in protection. It is possible to do this by hand at the controller level.

It is also possible and probably recommended, to consider resource-based access control libraries such as cancancan (cancan replacement) or punditto do this. This ensures that all operations on a database object are authorized by the business logic of the application. p.s. We wrote about these gems in our article The most used RoR gems for all the time.

CSRF (Cross Site Request Forgery)

This attack method works by including a malicious link in a page that accesses your web application that the user is believed to have authenticated. Ruby on Rails has a mechanism for CSRF tokens:

   class ApplicationController < ActionController::Base
       protect_from_forgery

Also, note that by default Rails does not provide CSRF protection for any HTTP GET request. There is a top-level OWASP page for Cross-Site Request Forgery (CSRF).

Mass Assignment

Starting from Rails 4.0 the problem of mass assignment is more or less solved. You can’t just pass input from the browser (that for example represent HTML form) and create new object in the database. Doing this in a code User.create(params) will lead to ActiveModel::ForbiddenAttributesError.

Make sure you always use strong parameters and explicit point out which parameters are accepted:  params.require(:user).permit(:name, :email)). Don’t permit all (params.require(:user).permit!).

Redirects and Forwards

Web applications often require the ability to dynamically redirect users based on client-supplied data. To clarify, dynamic redirection usually entails the client including a URL in a parameter within a request to the application. Once received by the application, the user is redirected to the URL specified in the request.

You should not blindly use user input as a target of page redirection.

if path = URI.parse(params).path
  redirect_to path
end

because it can lead to the XSS attack:

redirect_to params
   
   http://example.com/redirect?to=200&to=javascript:alert(0)//

Ways to make these attacks impossible in your app:

  1. Avoid using redirects and forwards
  2. Create a list of trusted URLs to sanitize input
  3. Don’t allow URLs as user input. Otherwise, make sure that each supplied value and authorized for each user
  4. Notify users that they are leaving your site and make them confirm the action for all redirects

There is a more general OWASP resource about unvalidated redirects and forwards.

Dynamic Render Path

In Rails, controller actions and views can dynamically determine which view or partial to render by calling the “render” method. If user input is used in or for the template name, an attacker could cause the application to render an arbitrary view, such as an administrative page.

Care should be taken when using user input to determine which view to render. If possible, avoid any user input in the name or use some filtering mechanism to be sure you are allowing to render only some limited scope of files.

You should never use user input to build a path for a template (partial) to render because it’s very easy to access any file in your application preparing right input for that attack.

Cross Origin Resource Sharing

Occasionally the need to share some resources across many domains appears. For example, you want to upload a file using AJAX request and send it to the other app. The receiving side should specify a whitelist of domains that are allowed to make those requests. There are few HTTP headers that control that.

You can use rack-cors gem and in config/application.rb specify your configuration like this:

module Sample
       class Application < Rails::Application
           config.middleware.use Rack::Cors do
               allow do
                   origins 'someserver.example.com'
                   resource %r{/users/d+.json},
                       headers: ,
                       methods: 
               end
           end
       end
   end

Sensitive Files

Many Ruby on Rails apps are open source and hosted on publicly available source code repositories. Whether that is the case or the code is committed to a corporate source control system, there are certain files that should be either excluded or carefully managed.

Don’t include these files to your repository:

   /config/database.yml                 -  May contain production credentials.
   /config/initializers/secret_token.rb -  Contains a secret used to hash session cookie.
   /db/seeds.rb                         -  May contain seed data including bootstrap admin user.
   /db/development.sqlite3              -  May contain real data.

Analyze how your application deals with sensitive data in the store. There are some simple precautions:

  1. Avoid storing sensitive data if it’s not necessary
  2. Don’t put sensitive data in your server code
  3. Either encrypt sensitive data or send it via an encrypted channel to avoid eavesdropping

Encryption

Ruby on Rails uses OS encryption, so you shouldn’t write your own solutions for encryption. Please, use only build-in libraries for encryption.

Devise by default uses bcrypt for password hashing, which is an appropriate solution. Typically, the following config causes the 10 stretches for production: /config/initializers/devise.rb

   config.stretches = Rails.env.test? ? 1 : 10

 

In RoR Cheatsheet you can also find an information about:

  • Security-related headers
  • Business Logic Bugs and
  • Attack Surface

Read more about security on the official RoR site.

Not to miss anything interesting subscribe to our weekly newsletter!

Rate this article, if you like it

Thanks! You’ve rated this material!

Got a project? Let's discuss it!

*By submitting this form you agree with our Privacy Policy.

Mailing & Legal Address

Syndicode Inc. 340 S Lemon Ave #3299, Walnut CA, 91789, USA

Visiting & Headquarters address
Kyiv Sofiivska 1/2a, 01001, Kyiv, Ukraine
Dnipro Hlinky 2, of. 1003, 49000, Dnipro, Ukraine
Email info@syndicode.com
Phone (+1) 9035021111