This is a blog post version of the Rails Security talk that I gave at Rubyfuza 2017 in Cape Town earlier this year.

I’ll also be giving this talk (or an updated version of it) for Ruby Dev Summit, a free online conference, in October this year.

If you don’t know who I am, I’m a (primarily Ruby) developer at Kisko Labs.

Kisko is a small software consultancy based in Helsinki, Finland. We’re heavily invested in Rails as it’s our primary development tool so Rails is near and dear to our hearts.

You can find me in all the usual places:

First some things you should know about me…

I tend to start too many side projects (and sometimes I even finish some of them)

I brew beer as a hobby

To help with my brewing hobby, I build an iOS app for browsing BJCP Style Guidelines.

I’ve also made Piranhas, a book price comparison app. It lets ou search for books on different stores and it calculates the shipping costs and currency conversions for you.

I’m also working on a product called TLS.care. It monitors your sites and alerts you if your SSL certificate is about to expire or if it has been revoked.

This is the ass covering section

I’m not a cryptographer, not even an amateur one.

I’m not a hacker of any colour.

I’m just a developer who want’s his apps to remain as secure as possible.

And with that out of the way, we can continue.

What is this talk (or blog post in this case) about?

Most of this is advice that will apply to any web app, regardless of what framework you’re using. However, there are some Rails specific implementation suggestions as that’s our primary tool at Kisko.

What sort of risks do we mostly deal with? Who is our adversary?

You might even be asking yourself “Why would anyone ever hack my website?”

In fact it’s likely that there won’t be a person targeting you but rather it’ll be a bot or script executing attacks against as many targets as possible.

Automation gives attackers all the same advantages that it does elsewhere:

  • Mass exposure
  • Reduced overhead
  • Tools for everyone regardless of skill
  • Dramatically increased odds of success

Some people might feel that their site doesn’t have anything of value to anyone.

This is a sentiment that is almost universally wrong. All legitimate sites have at least once thing of value to malicious actors: reputation.

Your site’s good reputation can be used to facilite malware installs, dodgy ads, or the attacker may simply choose to hold your site hostage.

At the end of the day, every site on the internet is a target to someone.

At Kisko we love Rails, it increases out productivity and let’s us achieve great things for our clients in a shorter time than we could otherwise.

How does it fare security-wise?

We certainly think it’s pretty swell…

It comes with sane defaults (both in terms of security and otherwise).

You get countermeasures against CSRF, XSS, and injection attacks straight out of the box.

It sets sane security headers by default

It comes with support for securely stored sessions and passwords and encourages you to adopt good development practices.

In fact, you get all of this for “free” when you use Rails.

If you work with Rails, you should read the Ruby on Rails Security Guide.

Assuming that you’ve chosen Rails and are using all the security features that it supplies out of the box, what more can you do?

First of all, and perhaps most importantly, you should use HTTPS in production.

I think we are well past the point where it’s acceptable not to use HTTPS for production apps.

Our clients or users might not know to require it, but as experts we know better.

Even if you believe that your site “has nothing valuable”, do you trust every actor that might attempt to inject content onto your page?

This is happening, and it’s being perpetrated by major ISPs in major countries. This is not some theoretical threat…

Studies suggest that ad injection affects as many as 5% of browsers.

HTTPS increases the chances that what you serve ends up in front of the user, unmodified.

Both Chrome and Firefox now mark login pages as “Not Secure”.

This will soon be the case for all pages with forms on them!

And the plan is to gradually extend this to all pages served over plain HTTP.

And if that isn’t enough, there are also technical reasons to use HTTPS. For example HTTP 2.0 is effectively HTTPS only and some advanced browser features require HTTPS.

What’s more, if some shady wifi hotspot injects ads or malware onto your site your users are going to blame you, not the network.

Hopefully by now you'e conviced that you need HTTPS. So how do you get HTTPS for your site?

Anyone can get a free certificate from Let’s Encrypt. If you use AWS, Amazon will issue your a free certificate for use on their services.

If you use Heroku, it already has support for automated certs from Let’s Encrypt.

Let’s Encrypt are also working on adding support for wildcard certs by January 2018

If you’re using Rails, you should make sure you enable the force_ssl setting.

This redirects visitors to the HTTPS version of your site, enables Strict Transport Security (HSTS), and marks cookies as secure.

HSTS means that repeat visitors will automatically use HTTPS after the first visit.

Secure cookies are only sent over HTTPS, never plain HTTP.

On my test site everything seems fine and dandy using the Rails and Nginx defaults.

That’s good as far as it goes, but how can we check if the defaults are good enough?

Luckily for us there’s a really handy tool we can use: the SSL Labs server test

It’s free and it does a great job of telling you were you might have gone wrong.

Well…

I mean it’s not bad…

…but we can do better!

If you click “learn more” get to read interesting things about logjam and FREAK attacks, DHE_EXPORT ciphers, Elliptic-Curve Diffie-Hellman Key Exchange and prime numbers.

Luckily for us there’s a shortcut.

People far smarter than me have created the Mozilla SSL configuration generator.

Pick your server and what browsers you’re targeting and…

…and you get a good server configuration template.

(also known as magic security incantations)

And once we deploy it to our test server…

An A+ is better than a B.

If your site meets the technical requirements, you can submit it for inclusion in the HSTS preload list. This list is used by Chrome, Firefox, Opera, Safari, IE 11 and Edge.

The requirements are pretty straightforward, so it’s not hard to meet them.

Moving on to content security policies.

This is how MDN explains what a CSP is, personally I found that nigh incomprehensible.

My version is that it’s a header which tells the browser where assets (scripts, stylesheets, fonts, and so on) can be loaded from.

Even IE supports it with an X- prefix.

A CSP header can be used to reduce the potential attack surface of your site.

Even with the fairly relaxed CSP that I’m forced to use on Piranhas, it’s blocked some weird requests.

Here’s a log of a script that was blocked from static.cmptch.com

And if you Google static.cmptch.com, you find some interesting results.

I’m still not sure what or why some thing was trying to load this on my site, but I do know that I want no part of it.

CSP headers are set per-response, so different pages can have different levels of restrictions.

For example, you can only allow scripts from the same origin as the page itself.

Or allow same origin and apis.google.com

There’s a large variety of directive available for allowing/blocking scripts, images, fonts, and more.

You can eport-uri directive to get JSON reports on what’s been blocked.

Report URI is a handy free service for collecting the violation reports.

That’s where this screenshot you saw earlier came from…

How should you go about adding a CSP header to your site?

Adding a CSP header to a long standing site can be tricky because you have to figure where you’re loading everything from. It’s much easier if you’re starting a new project

You might not even be aware of everything that’s being loaded (Facebook buttons, YouTube embeds, analytics, and so forth).

A simple policy like this might be a good place to start and if you want more tooling…

… I recommend the SecureHeaders gem from Twitter.

It provides support for CSP headers (and a lot more) and it also provides support for overriding the policy on a controller or action level.

Going through everything in SecureHeaders is too much to cover in this talk…

The defaults are pretty sane (but more strict than the Rails defaults).

The defaults are mostly the same as the Rails defaults, with a couple of extras.

The CSP header, X-Download-Options (which mitigates some IE vulnerabilities), and X-Permitted-Cross-Domain-Policies which restricts Adobe Flash player’s access to data.

And here’s the CSP header which didn’t fit onto the previous slide…

If this still sounds scary, there’s also a safe way to experiment.

As the name suggests, like the normal header except that violations are not enforced, only reported. You can see the violations in console or via the report-uri.

This is also great if you’re making changes to an existing policy.

If you embark on the CSP road, here are my tips on how to proceed.

Even an incidental look into the work of security quickly lands you in a sea of alphabet soup.

So here’s one more: HPKP

What is HTTP Public Key Pinning and should you use it?

It’s a security header that what public keys the browser will trust in the future.

I feel that this is an excellent way to shoot yourself in the foot.

You can lock out your users if you’re not careful.

Once it’s propagated to your users’ browser caches, the only recourse is waiting for it to expire.

My feeling is that it’s not worth the pain, I’m not the only one.

For a more detailed look at the issues with HPKP, see this blog post by Scott Helme (the creator of Report URI): I’m giving up on HPKP

How ever if you’re slightly crazy or work at an organization with the resources and discipline to handle it, here’s a couple of tips.

In short: be very, very careful. Make backups keys and store them offline. You may also need to anticipate switching to a different key algorithm in the future.

Wrapping all of this up…

Rails is great and you should use HTTPS.

Using a CSP is not very difficult, especially if you have one from the very beginning.

Don’t use HTTP Public Key Pinning unless you actually understand the risks involved.

Ping me on Twitter if you have comments (or via email)