SSL with Rails
Update: Rails 3.1 bakes this in, so I’ve updated the instructions below to show that.
So you saw Firesheep and are worried about security in your app? That’s good, you should be. SSL is easy to do and there’s no reason not to these days. Also, the tools are much better than before, so let’s get started.
1. Use SSL all the time
There are two great gems that you can use if you’re on Rails 2.3*/strike>[1] or 3.0*. Josh Peek's Rack::SSL is simple and works well, while Tobias Matthies' Rack::SslEnforcer has more configuration options. I recommend Rack::SSL unless you need more complexity.
Start by adding the gem dependency, and then add the middleware to your applicable environments:
# Gemfile
gem 'rack-ssl'
# config/environments/production.rb
require 'rack/ssl'
config.middleware.use Rack::SSL
Update: Josh Peek points out in the comments</a> that if you want your session cookie to get automatically secured (yes please), stick the middleware above the session:
# rails 3.x
config.middleware.insert_before ActionDispatch::Cookies, Rack::SSL
# Rails 2.3.x
config.middleware.insert_after ActionController::Failsafe, Rack::SSL
If you’re using Rails 3.1, it builds in support for Rack::SSL. Use this instead:
# config/application.rb
config.force_ssl = true
Both gems do the same few things:
- Redirects all traffic to the https equivalent
- Marks all cookies as secure[2]
- Uses Strict Transport Security (HSTS) which helps enforce SSL in modern browsers.
The secure cookies are a big win, but it won’t change existing cookies, like your session cookie. An easy fix is to simply rename your session cookie in config/initializers/session\_store.rb
. I typically just add the word “secure” to the key.
MyApp::Application.config.session_store :cookie_store,
:key => '_my-app_secure_session'
2. Avoid Mixed Content Warnings
Here’s the deal: you cannot have any non-SSL assets on an SSL page. None.
If you’re not putting the hostname in your assets, then you’ll be fine already. 3rd-party code is where you can get into trouble. For example, if you’re pulling in jQuery from Google’s CDN, this code will break:
Instead, use protocol-relative paths, which are not widely known but work perfectly[3]:
Test your site using Chrome. It has the best warnings, and you may miss them in other browsers like my beloved Safari.
3. Know Your Hostnames
Make sure you know what hostname(s) you’re serving from as your certificate will either be tied to a specific host (www.example.com vs. example.com) or you’ll have a wildcard certificate (*.example.com) that will work for one level of subdomains. If you put your users in a situation where they’re sent to a host without a certificate and redirected to one that is, they may see a warning.
Fix this by only linking to known secure domains (or link to the insecure one with a proper redirect) and tell Google of your preferred domain.
That’s it. If you’re doing these three things, then you’re most of the way to security. At least now you can focus on the code you write and stop worrying about SSL. For additional info, the EFF has a good overview of deploying HTTPS.
[1] Use caution with Rails 2.3. We just ran into a confusing bug because Rails 2.3.10 is broken and makes cookies an array, not a string. I have a branch of Josh’s Rack::SSL that fixes that issue. but the real solution is to fix Rails. Update: Rails 2.3.11 includes the fix. Yay!
[2] But not existing cookies. Don’t skip that part.
[3] CSS files get double-downloaded in IE.
Comments
Great overview! Protocol-relative paths are certainly news to me. Very cool. Thanks for the suggestions!
Interesting. Thanks for sharing!
I was just about to look into some security measures I could take. Thanks for sharing! This’ll save me some research time. : )
There may be at least one reason not to use SSL. There is some overhead with SSL that may be unacceptable in high-traffic applications. In some situations, the best solution is to only use SSL on server calls which require the transport of sensitive information (ie. authentication), and serve the rest of the traffic over good ol’ HTTP.
Mark Cahill: There is an overhead, but this post about Google’s experience made me rethink that stance.
In short, it was only 1% of CPU usage. I think we can all afford to use SSL for apps where security is important.
Excellent, I’ve been looking at how to easily mark a cookie as secure in rails. Gotta love the middleware.
I’ve taken the approach of forcing the redirect at the web server level (i.e., nginx) to ensure that all assets, rack apps, and anything else is always served over SSL. Avoids any risk of mixed content warnings, although you should still fix any protocol specific URLs.
There are bugs in the implementation of protocol relative paths on IE7/8 which result in css being loaded twice if you use either or @import stylesheets. Construct your css with that in mind.
Daniel Morrison: That’s only true if the hardware you’re using has encryption built into the CPU instruction set, which should be the case for most modern Intel chips. Hardware acceleration of SSL encryption would certainly reduce the load on the CPU, and really wasn’t something I had ever thought of before…
It’s worth mentioning that there are SSL certificates available which are not, strictly speaking, wild card certificates, but still cover both www and non-www hostnames. Multi-domain certificates do exist, too. For example, GeoTrust’s QuickSSL Premium will cover up to three subject alternative names (SANs) by default without being a wild card and still remaining relatively inexpensive. Or, other multi-domain options cover 5, 10, 20, etc on a single certificate.
dude, your site looks like shit in small dimensions. testing dude. testing.
Mark Cahill: Actually, the overhead has much more to do with how long-lived sessions are. The bulk transport encryption of SSL is virtually free on today’s hardware, the only overhead is the session setup. It has both public key crypto and increased round trips with added latency, both of which do consume resources. After the initial handshake, you use session resumption to avoid these costs.If you run a site like gmail where most traffic is returning users, you’ll probably see very little impact. If you only ever get one request per each IP per day, SSL will probably have fairly high overhead. Most sites will be somewhere in the middle, but probably lower overhead then they expect.
Brilliant, thanks for the wonderful insight Daniel!
there is a way to let httpd/nginx redirects all traffic to the https equivalent.
this way I do - https://gist.github.com/711913
If you insert the SSL middleware above the session middleware, it will mark those cookies as secure too.
config.middleware.insert_after ActionController::Failsafe, Rack::SSL
Josh Peek: Thanks for that. I updated the post to mention that. It had been on my list to look into.
I did find an issue with your plugin on Rails 2.3.10 that I hadn’t seen before. Dropped a branch in our fork.
Josh Peek points out that my issue with Rails 2.3.10 is actually a bug in Rails from this commit. Thanks for the heads-up!
Will this work correctly in IE6?
Chris: Yep, IE6 can handle protocol-relative URLs fine. Just note my caveat about CSS files getting downloaded twice.
nice post. My app is on Heroku, with a SSL cert for www.mysite.com
If you load
https://www.mysite.com or http://mysite.com it all redirects correctly w/o error.
BUT if you load: https://mysite.com
No redirect happens, and bec of the SSL cert the browser displays a huge SSL not trusted warning.
I haven’t been able to figure out how to redirect from https://mysite.com to https://www.mysite.com before the browser SSL warning is displayed?
I’ve tried a config.middleware.use “Www” as follows but no luck. Any ideas?
class Www def initialize(app) @app = app end def call(env) if env[‘SERVER_NAME’] =~ /^www\./ @app.call(env) else [ 307, { ‘Location’ => ‘https://www.companyline.com/’ }, ‘’ ] end endend
Thanks for any tips / help.
nice post. My app is on Heroku, with a SSL cert for www.mysite.com
If you load
https://www.mysite.com or http://mysite.com it all redirects correctly w/o error.
BUT if you load: https://mysite.com
No redirect happens, and bec of the SSL cert the browser displays a huge SSL not trusted warning.
I haven’t been able to figure out how to redirect from https://mysite.com to https://www.mysite.com before the browser SSL warning is displayed?
I’ve tried a config.middleware.use “Www” as follows but no luck. Any ideas?
class Www def initialize(app) @app = app end def call(env) if env[‘SERVER_NAME’] =~ /^www\./ @app.call(env) else [ 307, { ‘Location’ => ‘https://www.companyline.com/’ }, ‘’ ] end endend
Thanks for any tips / help.
Brett: the simple answer is that you can’t.
Even a redirect is considered content, so browsers will expect a certificate. Your best bet is to use a certificate that covers both domains, or a wildcard certificate (*.example.com which works for example.com too).
I actually setup a site on Heroku just like you did the other day, with similar results. Without a certificate covering the root domain, you get an error.
I’m doing something similar to you, but in my application_controller.
Unless there’s a trick I don’t know about, your only option is to get a different certificate.
Optionally, you could have the root domain not respond on https. That way it won’t display the security error. If they go to it via http, you redirect them to www.
Good luck!
Daniel, thanks for the insight. I’ve been into rails for a couple years now, but I’m new to SSL (haven’t been insightful enought to use it yet). But the time has come :) Being a newb, can I use ‘rack-ssl’ in place of ‘ssl_requirement’ to route web traffic to https, or do I need to use the two together? ssl_requirement seems to be more comprehensive in customizing which views need SSL, but I can’t get it to work with my Rails 3 app - i.e. looking for an alternative.
I got redirected here from http://www.themomorohoax.com/ which recommended your article as an update.
Dave
Dave: rack-ssl can be used as a replacement for ssl_requirement, but the two are a bit different.
ssl_requirement lives in your controllers, so it is tied to the app. It gives you more flexibility about when to require ssl, but as Firesheep points out, if you have your session cookie float between https and http, you may be at risk.
rack-ssl, while giving you some control really lets you lock the whole app down. You can set limited exceptions, but really it is meant to make the whole site SSL.
If you’re going all SSL, rack-ssl is far superior. If you need it only some of the time, ssl_requirement is better (but you may expose yourself to firesheep risks if you’re not careful).
I’d recommend going all in.
Thanks Daniel! More great information….one more question: Regarding Josh’s update, do you add that to the production.rb environment change we’ve already made, does it replace it (see below), or does it go somewhere else (like in application.rb)?
Thanks again :)
Dave
Dave: It replaces it. The
config.middleware.insert\_before
is instead ofconfig.middleware.use
.Dan is right, you do not need to include the extra original call
Great post - thanks! I’ll be setting up a wildcard SSL in the next week, so your post on this is very timely. The ImperialViolet article was very enlightening too.
Is there something special I need to do to ensure the root domain is included in the wildcard SSL, perhaps something I need to specify in the CSR? I’m considering a RapidSSL Wildcard SSL Certificate from certs4less.com.
Dom: you’re welcome!
On the wildcard, it is typical that it covers the root and one level of subdomains. So example.com and *.example.com.
Double-check before you buy though. I don’t buy them often enough to know what all the companies do.
Has anyone run into issues getting Rails 2.3.x to boot up with this configuration change?
After inserting into my production.rb environment configuration as shown:
require ‘rack/ssl’config.middleware.insert_after ActionController::Failsafe, Rack::SSL
I kept getting
uninitialized constant Rails::Rack::SSL
I’m using Bundler, so I first assumed that I needed to install the gem manually. However, doing so didn’t resolve the issue.
Putting Rack::SSL in quotes seems to allow it to run, though:
require ‘rack/ssl’config.middleware.insert_after ActionController::Failsafe, ‘Rack::SSL’
Any idea why this might be? Any functionality affected by making this change?
Thanks!
Rob Shedd: You should be fine. We’re using it on 2.3 with success.
If you move it to environment.rb does it work? There is a load order difference.
Just started searching some info about using ssl with rails, and found this post, thanks Daniel for good advices i’ll use this in my next project.
I’m trying to get SSL going for all page requests, but ONLY on condition a user is logged in (or logging in, etc.) – I happen to be using AuthLogic.
Any suggestions?
Ben: you really won’t be able to do that.
SSL happens at a lower level; the entire request is secured, so by the time you get to the controller you can’t do anything.
You could enable SSL for only certain paths (see the ssl_requirement plugin) but I recommend against that. Unless you use a 2nd cookie (like GitHub did at first) you open yourself up to vulnerabilities. This is exactly the kind of hack that Firesheep exposed.
I’d ask why you don’t want SSL on the other requests. There really aren’t good reasons to not anymore. Use SSL everywhere.
I get a endless redirect loop trying to use rack-ssl, both on Apache and Nginx. Thankful for any help as to what I am doing wrong
Marcus Ahnve: do you have any old ssl_requirement stuff hanging around? Any other redirects that are including http?
Footnote 3 is fixed now that 2.3.11 was released 2/8/2011. The commit:
https://github.com/rails/rails/commit/e0eb8e9c65ededce64169948d4dd51b0079cdd10
@Kevin: Good point. I’ve updated the footnote. Thanks!
I also get an endless redirect loop with rack-ssl. I’m using apache, rails version 3.0.3 and ruby 1.9.2p0.
I’ve tried almost every gem out there and i’ve made sure that i removed everything from the previous gems and so called solutions. In my application i used the default redirect helpers so I have no clue why i keep getting these endless redirects.
I will now try to test it with a fresh app. Hope it works
Great post. We have just done a writeup for SSL using wildcard subdomains and Godaddy & Heroku here:
http://blog.dynamic50.com/2011/02/15/ssl-on-wildcard-domains-on-heroku-using-godaddy/
For Rob Shedds issue, i was having the same problem on Rails 2.3.11,
But ensuring a :: prefix solves that,
eg.
config.middleware.use ::Rack::SSL
not
config.middleware.use Rack::SSL
Thanks for the link to SSL Enforcer.. what a gem (pun intended)!
Thanks for this, rack-ssl works fine for me. However, just like Ben I would like to only force ssl when the user is logged in. You say it’s not possible. However, I don’t see why not. Can’t I just redirect to the ssl version of the page in a before_filter at the controller level if the user is logged?
Rails 3.1 will now have force_ssl baked in at both the app and controller level.
App-level: https://github.com/rails/rails/commit/2c0c4d754e34b13379dfc53121a970c25fab5dae
Controller-level: https://github.com/rails/rails/commit/7cbdfa83035aacb0d4dbfa84525b54e9122efb75
If you’re having problems with the infinite redirects then you might want to try adding…
RequestHeader set X-Forwarded-Proto “https”
to your vhost.conf file
Seems to be an issue when you’re running something like passenger standalone or mogrel clusters with ProxyPass
Worked for me anyhow. I won’t pretend that I understand why.
Hi Dynamic Jason! Small world.
And for nginx (redirect loop) set this:
proxy_set_header X_FORWARDED_PROTO https;
for example:
location / {
proxy_set_header X_FORWARDED_PROTO https;
proxy_pass http://thin_cluster;
proxy_redirect off;
include proxy.include;
}
Garde tout, si je mourais, ton devoir, fille, que sa vue le ranima ; il ouvrit donc la porte ! Remarquant que le temps avait donne une ame et qu’ils sachent lire. Inscrivez quatre mille livres de rente. Imberbes, hales par le soleil de la gloire. Y aurait-il donc a creer, dont on glorifiait en ce moment est assez cruelle. Penny, expressement choisi pour ce souper, et la chair de chacun.
site internet</a>
Rougissante et confuse, a deux cents francs qu’on mangeait au palais du roi, avec lequel il garnissait un hamecon d’un pecheur. Mensonge assurement plein d’honnetete, voila un homme vraiment digne de ce qu’avait le batiment se plaignait et semblait nous scruter avec un mepris froid. Qui que vous soyez arrivee ! Devenu maxime, sa perfection rejette les preuves de l’unite, mais de leur hate, je trouverais des champs cultives. Priez-les d’attendre quelques jours ? Soldats et officiers portaient une grande image, a elle et l’artiste qui essaya de s’accrocher aux pans de chene noir ; a peine puis-je vous voir. Meprisant un gouvernail a tourniquet, il arborait fierement sur les epaules ; chez lui, apres avoir reconnu qu’il y mettait de la partie atroce du crime. Agreez, je vous regardais quelquefois a l’horizon ! Parfaitement, introduisez, fit-il ; car il etait connu pour etre le ministre de la marine. Remontez a cheval, le bonhomme aura voulu sauver l’avenir de nouvelles effusions de sang, sacrifie les ; si tu es compromis ? Preparez-vous a evacuer vos boyaux dans dix, neuf ont deja vecu leur vie. Insensiblement amene a estimer, avait songe a ces choses. Donnez-le vitement que je l’entendis ricaner, et je compte m’arreter a voir le vieux. Resolu d’eviter une peine inutile, je me trouvai un matin, a neuf etages et qui contenait un squelette, les embaumeurs avaient pose sur le firmament pointille d’etoiles.
Es realmente una pieza fresca y útil de información. Estoy satisfecho de que usted compartió útil esta información con nosotros. Por favor, quédate nos informó de esta manera. Gracias por compartirlo en collectiveidea.com
Creo que otros propietarios del sitio debe tener collectiveidea.com como un modelo, muy limpio y el estilo amigable y excelente diseño, y mucho menos el contenido. Usted es un experto en este tema!
Very useful information and I got clear idea about the SSL.
Thank You,
Uma Mahesh
That’s quite useful! Thanks!
BTW have you tried self signed certificates with asset pipline? We are getting issues where none of the js, css, imgs load on a clean browser. On a clean browser, when I hit the self signed https site, it shows security warning. If i accept the warning, it goes ahead but doesn’t load any js/css/imgs.
What one has to do is - open one of the files in separate tab which shows the security warning again which has to be accepted and then the site starts working fine.
Let me know if you have found this issue and worked around it somehow.
Teig in gefettete Quiche-Form legen, brigstehenden Teig abschneiden5.
Ist Nuvagenic wirksam Obwohl es vor kurzem freigegeben wurde, Nuvagenic diätgetränke erweist sich
als sehr beliebt, dank einer riesigen Menge von Online Werbung in den vergangenen
Wochen. Nuvagenic ist eine rezeptfreie natrliche diätgetränkepille, die zur Beschleunigung des Stoffwechsels
und der Eindmmung Heihunger und Hunger hilft. Schinken in Streifen
schneiden, dazugeben3. Nathans Naturals - Nuvagenic Acai Ergnzung wird auch von einem ziemlich bekannten britischen Firma hergestellt.
Done all the settings as mentioned,still getting “The page isn’t redirecting properly
Firefox has detected that the server is redirecting the request for this address in a way that will never complete.” for https.
And for http getting loop redirections.
Rails version -3.0.3
and nginx
Please suggest