Handling SQL injection and XSS in Rails

I've been using Ruby on Rails for a good year and a half now, but something I still remember from when I was just getting started was the lack of discussion about SQL injection or XSS issues. This isn't very surprising; in any language or framework the last thing you explain to beginners is a rousing study on security concerns. It's kind of sad, really, when you think about it. But so is life. Let's look at code.

SQL Injection

Your basic ActiveRecord find goes something like this:

@articles = Article.find(:all)

That'll find all of your articles and populate them into @articles. Cool beans. Say you want to grab all the articles that are titled a certain way, which you have access to via params[:title]. There's a few ways of doing this. One way is to add :conditions.

@articles = Article.find(:all, :conditions => "title = #{params[:title]}")

So what you're doing is grabbing :title from your parameters and then directly tossing it in your query. Repeat with me: bad code. This is vulnerable to what's called an SQL injection attack, meaning anyone can come along and look for a post with the title of:

1; DROP TABLE super_cool_table;

So, like, don't do this.

One solution is to use ActiveRecord's magic, super-cool finder:

@articles = Article.find_by_title(params[:title])

This is fine. Rails automatically makes sure that any arguments you pass in won't be able to inject arbitrary SQL. For a bit more flexibility, I go the following route:

@articles = Article.find(:all, :conditions => [:title => params[:title]])

...or even:

@articles = Article.find(:all, :conditions => ["title = ?", params[:title]])

These are both a-okay. Rails sanitizes the parameters in each of these scenarios, so you can rest assured that you're not wide open to some ugly attacks.

XSS (Cross-Site Scripting)

I recently discovered a XSS hole in PayPal's own website, which theoretically would let me set up an attack that would let me steal your cookies, hijack your PayPal session, and do lots of Bad Things to your PayPal account and linked credit cards and bank accounts (probably involving hookers, a flight to Panama, and a fancy new yacht). (By the way— if anyone has a better idea of getting in touch with PayPal, let me know... it's surprisingly difficult to sift through regular customer support with issues like this.)

Anyway, I digress. For more information on XSS, check out the ever-popular Wikipedia article. You're here to combat XSS anyway, right? Let's get started.

In your views:

<%= h @article.title %>

That's it. The method "h" escapes any HTML that might be in your article's title that a wayward user might have submitted.

Granted, it's a bit of a pain in the ass. Django decided to auto-escape your values by default, and Rails does not. And the fun with XSS is that if you miss one of those little h method calls, well, you're screwed.

There's a few ways around that, though. One is to make sure you sanitize user input on the way into your app, so when you call it later on you know that you're safe. Another somewhat unequal way of doing things while maintaining interactivity (like embedded links and images) is to do the same thing I'm doing on this blog for its comments: allow users to use Markdown (or Textile) instead of HTML.

Another option is to go the plugin route and let it handle sanitization for you:

A tip for all you budding entrepreneurs

You've built (or taken over) a hotel in town. It's quaint, nice, and not too shabby. Figuring you want target the hipsters and traveling businessmen, you hook up each room with a steady stream of wifi and ethernet jacks. You also install a super cool server to track all fun things related to billing and invoices and fun things.

In the midst of all of these changes, just make sure you don't share the drive of your server so that anyone who plugs into your network can see your invoices and can muddle with your billing software.

I'm just saying.

In other news, I just took my first flight on jetBlue yesterday. What a great experience. Everyone else in the American airline business (with the exception of Virgin America) just doesn't get it. Also, FF Din is fantastic.

(No, I did not muddle with anything. Sheesh.)

Internet access on the road

I'm currently running all around, and I've found that it's still a pain to stay connected when you're on the road. Hotels have cumbersome wireless access, there's a lack of wired access, you have to pay sometimes, the signal can be weak or incompatible, and so on. I just wish business owners would start seeing how much of a benefit you could provide to your customers if you offer free wireless without any intrusive sign-ups or anything like that. Or better yet (though it's a substantial infrastructure investment), provide ethernet jacks in every room.

Although, there still is the problem of security. I'm sure I always overreact and there really aren't people sitting in the parking lot sniffing passwords, but I still know how easy it is to do and I'm always wary of what data I put out there on open networks. Right now I'm running off of Verizon's EVDO network through a USB connection with my phone, and that tends to solve all of those problems, but there still should be additional focus on all of these issues (and specifically security will be one that people will be increasingly worried about into the next five years).