TechTalk: Implementing SSL on a mixed SSL Ruby-on-Rails app on Heroku

18 Jul

In the last month, we’ve taken some big steps towards getting the Ubersense back-end ready for the scale that we look forward to having. One of the first things we knew we had to move over from our old EC2 setup was our SSL support for secure parts of the site. Due to some limitations with using a shared host like Heroku, we decided to use a dedicated sub domain for secure connections: secure.heroku.com. We bought the certificate and got it set up using Heroku’s new SSL Endpoint add-on.

Not quite done though. Using just the simple SSL add-on, what we figured was that users would log in at https://secure.ubersense.com/login (good) and then stay on that host over http when navigating the rest of the site (not so good). We used sslEnforcer and its strict option to make sure that any non-secure requests to the secure actions redirected users to the secure host. Unfortunately, sslEnforcer doesn’t have a way to take the opposite effect and redirect non-secure requests to a different host.

After taking a look through the sslEnforcer code, it was apparent that we needed the effect of redirect_required?, but with the opposite conditions (non-ssl request on secure.ubersense.com). At first we looked into some rack middleware code that did just that, but we couldn’t find anything already written that solved the problem. We considered writing our own middleware, but that seemed like overkill, and the solution was really quite simple.

We decided that the best solution was a simple before_filter. If we’re on the secure domain, but it’s not an ssl request, just redirect to the base domain. Simple enough, 3 lines of code, which is made very easy by Rails, and we were done!

if !request.ssl? && request.host.match(/secure/)
redirect_to :host => BASE_DOMAIN
end

But we needed to do one last thing to account for the user’s session. By default, a session on secure.ubersense.com will be different than a session on ubersense.com because cookies store the session identifer. We need the cookies to be shared across those domains, so we tell our session cookies to use the base domain and ignore the sub domain. In the session_store initializer, we just need to set the :domain => :all option to use ubersense.com for all of the cookies:

UberSense::Application.config.session_store :cookie_store, :domain => :all

Once that was set, users can log in on the secure host and be redirected to the non-secure host with our session in tact. Success!

We have scoured StackOverflow and Rails message boards looking for this solution, but we couldn’t find anyone who had solved this exact problem.

How have you solved this type of problem? Please share below.

Leave a comment