Chapter 18. Securing, Managing, and Deploying Your Rails Projects

When most people think about building a web application, they think about the design, programming, debugging, testing, and all the work that has to happen before an application goes live. Writing the code—while, of course, critical—isn’t the only major technology puzzle that has to get solved before an application runs. Bringing that application to the public (or even to an intranet) requires a few more critical steps that are as much about system administration as about code. The Rails framework approach is quite different from the usual CGI or PHP approach, so there are a fair number of Rails-specific issues you need to address.

First, you need to be prepared to battle the hostile nature of the Web. Every publicly exposed application will be tested and tried by a variety of visitors that you may not want or like, and even private applications sometimes face challenges from users. Using Rails isn’t particularly dangerous, and a lot of key techniques for protecting your applications from harm have already been covered. Nonetheless, it’s worth reviewing some Rails features that can be especially helpful.

Securing Your Application

It’s best to consider your application’s security before deploying it rather than after. It’s much easier to test for security leaks in the relative privacy of development mode, when only the schedule is a likely obstacle, rather than in a publicly available installation with real users who will miss the service if it goes away for a while.

The biggest risk for any application is data coming from outside of your own trusted self. As most applications invite users to put data in, this is an extremely common issue. You shouldn’t, of course, seal your application off from outside data—instead, you should always treat the data with suspicion, only trusting it after inspecting it carefully or keeping it in forms where even the worst attacks can cause no harm.

In Rails application building, this means that you should never ever trust any data in parameters (or any other data you didn’t write by hand yourself into the code), as it likely comes from outside of your system. There are many different angles where this can be an issue.

SQL Injection

Most applications built on databases rely on Structured Query Language (SQL) to get data in and out. Even if you’re relying on Rails to handle all of your database interactions, Rails still uses SQL to communicate with the database.

To understand what’s going on, start with a simple find call:

email = params[:email]
User.find(:first, :conditions => "email = '#{email}'")

If params[:email] contains "[email protected]", then the substitution in the double-quoted string used as a value for :conditions will produce:

email = '[email protected]'

Using that, Rails executes a SQL statement against the database, like:

SELECT * FROM users WHERE (email = '[email protected]') LIMIT 1

That works nicely. However, imagine if params[:email] contained "' OR 1) --". Rails would then execute:

SELECT * FROM users WHERE (email = '' OR 1) -- ') LIMIT 1

The -- is a SQL comment, causing everything after it to be ignored. Suddenly, an attacker can retrieve all of the email addresses stored in your database. To avoid this and other serious scenarios, change the way that you pass conditions to a query:

User.find(:first, :conditions => ["email = ?", email])

Passing the :conditions to Rails using the array notation will, as noted at the end of Chapter 4, tells Rails to sanitize the value of email before including it in the query. If you’re letting Rails create your SQL, that should be all you need to do. If, however, you cannot avoid creating SQL yourself, as you need to use find_by_sql, then use the model’s quote method to do the necessary escaping. That might look like:

User.find_by_sql "SELECT * FROM users WHERE email =  '#{User.quote(email)}'"

It’s always best to explicitly ask Rails to check on parameters you don’t trust.

Cross-Site Scripting

Cross-site scripting lets users attack other users. The attacking user enters potentially harmful HTML into your system, which is then displayed directly back to other users.

For instance, look at this code from a view:

<p>Email address: <%= user.email %></p>

This works perfectly well if the email address is innocent. But what if a user set his address to be <script src='http://example.org/my_evil_script.js'>? When the page is displayed, anybody could fetch and execute the remote JavaScript, which could perform any number of unpleasant functions.

To avoid this you should always escape content on output, as in:

<p>Email address: <%=h user.email %></p>

Of course, your models should always do the tightest possible validation, which would have noticed that the script element wasn’t an address. For plain text fields, however, there’s not a lot more you can do with validation.

To help you remember and test this, there’s a handy plug-in you can use called safe_erb (http://agilewebdevelopment.com/plugins/safe_erb). It treats any data coming from parameters, the database, or files on the disk as suspect until it’s untainted by means of the h escaping in views. Instead of displaying the suspect data, the safe_erb plug-in will cause your program to display an error. You will develop good habits very quickly when using this!

There are a few other options for dealing with markup in parameters. Sometimes you do need to display HTML in the output, to allow people to use certain tags such as <b> or <i> in content, for example. Rail’s sanitize helper lets you control exactly what can get through or not—allow <b> and <i> but forbid <script>, for instance. Other times you just want to strip all markup and only keep the plain text. Then, you probably want to use strip_tags, which removes all HTML tags.

Cross-Site Request Forgery (CSRF)

CSRF is an attack often used for gaining unauthorized access to password-protected websites. If an attacker can persuade you to log into a web app (pretty much everybody leaves themselves logged in all the time these days anyway) and then visit their web page with a malignant JavaScript, that script can fake a form POST to the web app using your existing session and cause destructive behavior.

The way to avoid this problem is to have every form include a special token that guarantees to the application that the incoming POST was in response to a page that came from that web app specifically. Fortunately, Rails has this protection built-in. Open up app/controllers/application.rb and you’ll see:

   # Uncomment the :secret if you're not using the cookie session store
   protect_from_forgery # :secret => '468e1168cef232cb93c2d56919e9fe4f

You should follow the comment and remove the highlighted # if you’re using the database or filesystem for your sessions.

If you view the source of a form generated from Rails, you’ll see the magic token:

<form action="/people" class="new_person" id="new_person" method="post"><div
style="margin:0;padding:0"><input name="authenticity_token" type="hidden"
value="f80a01b9f14d38e0816877e832637e3cc9e668a1" /></div>

One thing to note here is that if you’re generating your own forms, rather than relying on Rails’ help, you’ll still need to include the hidden form parameter manually. This can especially be a concern when Ajax code on the browser is generating requests.

URL Hacking

Predictable environments such as Rails leave themselves open to people being able to read more than they ought to. This can be very convenient in development mode, but creates major problems when applications are exposed to real users. Consider the following controller, where show_secret_stuff gives logged-in users (as set up in Chapter 14) a place to see their secret stuff:

   def show_secret_stuff
      @user = current_user
      @secret = Secrets.find(params[:id])
   end

Typically, the secret objects will have sequential IDs in the database, like 5, 6, 7, etc. We assume that the user will land on this page from a list of possible URLs generated in another page, like:

   <% @user.secrets.each do |secret| %>
     <%= link_to secret.name, :action => 'show_secret_stuff', :id =>
secret %>
   <% end %>

However, there’s nothing to stop them from incrementing that id in the URL to explore other people’s secrets, too. The controller just shown didn’t take any steps to make sure the secrets actually belonged to the current user. The answer is to ensure your finders are always as specific as possible. In our case the controller should instead say:

   def show_secret_stuff
      @user = current_user
      @secret = @user.secrets.find(params[:id])
   end

Getting the secrets through the @user object rather than directly will ensure that users can’t see other users’ secrets. The most important time to watch out for this is always when you’re doing resource nesting—i.e., showing anything that’s part of a has_many relationship. Never assume that your users are friendly, and always check as much as possible before letting a controller’s action do its work.

Other Security Issues

Security is never complete. For a lot more information on potential vulnerabilities, check the Ruby on Rails Security Project at http://www.rorsecurity.info/ regularly. The other key point to remember, though, is that your code and Rails itself are only two layers of a complex system that makes your application possible. Attackers can assault flaws in your operating system, your web server, or your database, not just your application. Always keep an eye out for security notices and updates, and ensure your operating system stays up-to-date.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset