Using SSL in Rails Applications

If your web application manages any private information, you should be using SSL (https://) for those parts of the application. Any data sent over a plain http connection can be “sniffed” by any ISP along the route that the packets take if you aren’t using SSL.

Fortunately, implementing SSL isn’t too hard, once you know what you need to do. This article will talk you through the process. These are the steps we’ll go through:

  1. Create your private key
  2. Create your certificate signing request
  3. Get your certificate
  4. Configure your web server
  5. Set up your Rails application

Creating your private key

The first thing you need is a private key, with which your certificate will be encrypted, and which will be used to encrypt your SSL pages before they are sent. You make the private key yourself. Assuming your server has openSSL installed, enter the following command in an SSH shell:

openssl genrsa -out domainname.key 1024

This creates your private key in the file domainname.key (you should substitute your domain name for domainname, of course, although the name doesn’t really matter). This key is unprotected, so you should ensure that this file is readable only by the root user:

chmod 600 domainname.key

Alternatively, you can create your key in an encrypted form that will require you to specify a passphrase when creating the key, and to enter that passphrase to access the key:

openssl genrsa -des3 -out domainname.key 1024

This makes your key more secure, but it has a big downside: once you’ve installed the key, you’ll need to enter the passphrase any time the web server is rebooted. If you’re not around and there’s an emergency reboot of your server, your site will be down until someone can log in and enter the key.

If you do encrypt your key, don’t forget your passphrase! Without it, your key, and the certificate that uses it, will be worthless, and there’s no way to recover it.

Creating your certificate signing request

Now that you have your private key, you can create the certificate signing request (CSR), which you’ll need to get your certificate.

In the SSH shell on your server, create your CSR as follows:

openssl req -new -key domainname.key -out domainname.csr

You’ll be prompted to provide the name of the company, the country, city, and state, and some other information. The most critical piece of information here is the common name, which must exactly match the domain of the server on which the certificate will be used, including any subdomain. Note that www counts as a subdomain, so one certificate works either with www.domainname.com or with domainname.com. You can also buy a “wildcard” certificate, which works with any subdomain, but they’re much more expensive (at this writing, $199 vs. $19.99 at GoDaddy). (To make your site work with or without the www prefix, you should be redirecting accesses to one or the other, so you’ll only need a certificate for one of them.)

Don’t enter any of the “extra” information that you’ll be prompted for. In particular, don’t enter a challenge phrase, or the certificate signing authority won’t be able to read your request.

The file domainname.csr will now contain a string of text, which you’ll shortly be providing to your certificate authority. The file contents should look something like this:

-----BEGIN CERTIFICATE REQUEST-----
MIIBxjCCAS8CAQAwgYUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UE
BxMKU2ViYXN0b3BvbDEXMBUGA1UEChMOTXkgV2ViIENvbXBhbnkxGzAZBgNVBAMT
End3dy5kb21haW5uYW1lLmNvbTEeMBwGCSqGSIb3DQEJARYPbWVAbXlkb21haW4u
Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpyw/LtDRQo/MCtDlckqLz
Cghnod7OFjcGXqprRW15Vn8bTB4v2L29lx2n+U1uK9jWCO/bZFUzVDZ/ESWhCRN8
roqbNuCxBdAzpX2M92RPcZWPeK+cRaJ7kafn5B8kyTXHenYWBAu/epy1NG7fagoO
qV4nqmCv0EwTkeWm9uShjQIDAQABoAAwDQYJKoZIhvcNAQEEBQADgYEAgXpQ6E0/
SyX7r25VI1EoLz2lRBX6tkqhOoBlVAPzDVN88todMaLQlbCz0VXKP9eS78cZe8kJ
7jI+Ujmio7GQ8zFLwpMCXzGCkpri30wO4hsK1lfvC/ScPTEayISECUNlTlRbRztW
W7AH4Z67d47E1hctoitbXSCbQVOfGavm1mA=
-----END CERTIFICATE REQUEST-----

Getting your certificate

Now you need to choose where to get your certificate, and what level of verification to pay for. To understand the choices, keep in mind that an SSL certificate provides two separate functions:

  • It certifies, to varying degrees, the identity of the person or business that controls the web site.
  • It provides the public and private keys for encrypting communications with the site.

You can get certificates at prices ranging from less than $20 to more than $1500, and they all provide the same encryption. The only difference is the degree to which they verify that you are who you say you are, and the seal that they give you to display on your web site. For more on certificates and certificate authorities, see Implementing SSL. Or, if you don’t care about all the fancy stuff (which, for the most part, provides little value) just go to GoDaddy and get your $19.99 Turbo SSL certificate.

The workflow will vary depending on your certificate authority; I’ll describe how it works at GoDaddy.

When you purchase your certificate, you get a credit that is good for one certificate. You then log into the SSL Certificate part of the site and use the credit to request a certificate. The web form provides a form field into which you paste the text from the CSR you generated in the previous step.

When you submit the CSR, the certificate authority will email the administrative contact for the domain to ask if they want to approve the request. This is how they verify that the certificate belongs to the domain. If you purchase a more expensive certificate, at this point they’ll perform additional verification steps to ensure that the certificate belongs to who it says it belongs it.

Once the CSR is approved, you’ll get an email with a link to download a zip file that contains the two certificate files you need to install on your server. One is your certificate, and will be named something like “domainname.com.crt.” The other is the intermediate bundle certificate that establishes the chain that connects your certificate to the signing authority. In GoDaddy’s case, this is gd_intermediate_bundle.crt.

Now is a good time to make a backup of these files. The intermediate bundle is a standard file that you can usually get again, and you can request that your certificate authority re-issue your certificate file. But if you lose the key file, your certificate is worthless, and you’ll have to purchase another one.

Configuring your web server

Now you need to configure your web server for SSL. Just how this is done depends on what server your using, its version, and how it was installed and configured. I’ll describe how it works for a typical Apache 2.x configuration; you may need to adjust this for your situation.

You should already have virtual host definition, either in your httpd.conf file or in an application-specific configuration file that is included into the main httpd.conf by reference. There are two kinds virtual host definitions: name-based, and IP address based. You can’t use name-based virtual hosts with SSL, so you’ll need to have a dedicated IP address for the site that is using SSL. If you only have one IP address for your server, you’ll only be able to have one application. If you want to host multiple applications, your host should be able to provide you with multiple IP addresses.

You’ll need two virtual host blocks, one for the non-SSL parts of the site, and another for the SSL parts. The basic configuration should look something like this:

<VirtualHost 123.456.789.123:80>

  Include conf/apps/domainname.conf.common

</VirtualHost>

<VirtualHost 123.456.789.123:443>

  Include conf/apps/domainname.conf.common

  # SSL Engine Switch
  SSLEngine on

  # SSL Cipher Suite:
  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL

  # Server Certificate
  SSLCertificateFile /etc/httpd/conf/ssl.crt/domainname.com.crt

  # Server Private Key
  SSLCertificateKeyFile /etc/httpd/conf/ssl.key/domainname.key

  # Server Intermediate Bundle
  SSLCertificateChainFile /etc/httpd/conf/ssl.crt/gd_intermediate_bundle.crt

  # Set header to indentify https requests for Mongrel
  RequestHeader set X-Forwarded-Proto "https"

  BrowserMatch ".*MSIE.*" \
    nokeepalive ssl-unclean-shutdown \
    downgrade-1.0 force-response-1.0
  
  CustomLog logs/domainname.com-ssl_log \
    "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

</VirtualHost>

Let’s walk through this. The first virtual host block is essentially the same as what you’d have without SSL, except that you have to specify an IP address (123.456.789.123 in this example). I’ve put all the configuration that would normally go here, to set up the proxy to Mongrel and any mod_rewrite rules, into an include file, domainname.conf.common, which is not shown here. Doing so allows the same commands to be reused in the SSL block.

The second virtual host block defines the same IP address, but this time for port 443, which is the standard SSL port. First we include all the common configuration code to handle mongrel and so forth. Then, finally, comes the SSL configuration directives. We need to turn on the SSL engine, specify the encryption to be used, and point to the three files we generated earlier: the key, the certificate, and the intermediate bundle. (The CSR isn’t needed once the certificate has been generated.) In this example, I’ve shown them going in subdirectories of conf, but you can put them anywhere, as long as these pointers match the location.

Next, we set the request header to tell mongrel when there is an https connection. Don’t leave this out, or you’ll have no end of confusing problems in your Rails code!

Finally, there’s a bit of config to keep Internet Explorer happy, and a custom log definition to log SSL accesses to a separate log file (this last bit is entirely optional).

To test all this, enter

apachectl configtest

in an SSH shell. This will check your config files for syntax errors without actually rebooting the server, so in case there’s a problem you don’t leave the server disabled while you sort out any errors.

At last, you can now reboot Apache (sudo service httpd restart), and if everything is correct, you’ll have an operating SSL server.

Tell your Rails app where to use SSL

With the above setup done, you should be able to browse to any page in your application using either http:// or https://. You’ll probably want to leave parts of the site unsecured, since SSL pages put extra load on the server, so you need a way to indicate which pages require SSL, and which don’t.

The ssl_requirement plugin, written by DHH himself, makes this simple. Install the plugin:

script/plugin install ssl_requirement

And then include it at the top of your application.rb file, which effectively includes it in every controller:

include SslRequirement

Now all you need to do is specify, at the top of each controller, which controller actions require SSL:

ssl_required  :login, :account, :payment, :cart

Be sure to include in this list any create or update actions that process form data from SSL pages. The magic of this is that you don’t need to change any links; any access to an action that requires SSL will automatically redirect to https://. If anyone tries to access a page that is supposed to be secure with an http:// link, they’ll be redirected.

You can also provide an ssl_allowed declaration if there are actions that you want to be able to access either way. Such actions won’t redirect to https:// but if that protocol is explicitly specified in a link, it will work.

Check your Ajax actions

For the most part, that’s all there is to it on the Rails side. There’s just one more thing that may require your attention: actions that service Ajax requests.

On any page accessed with SSL, all Ajax requests must use SSL, or they will fail. To make this happen, all you need to do is include the names of the actions that service the requests in your ssl_required statement.

In some cases, you may have Ajax actions that have no code in the controller, but are just rendering RJS templates or other views. For these actions, you’ll need to add an empty action definition block in the controller, and add the action name to the ssl_required statement.

One obscure case is the text_field_with_auto_complete helper. If you’re using this in your view, you’re probably also using the auto_complete_for :model :field shortcut in your controller. This automatically produces an action to respond to the autocomplete requests. But since the action isn’t explicitly defined, how do you tell it to require SSL? You just need to know what the automatically created action is named, which is auto_complete_for_model_field. Just substitute the names of your model and field, and add this action name to your ssl_required statement.

Check for mixed content

One last thing to watch out for is mixed content. If you’re testing in Firefox, you won’t notice it, but in IE, users will get a security warning if an SSL page accesses any non-secure content. You don’t have to worry about links to images and so forth, as long as they’ve been written as paths without the full domain name; those will automatically use the protocol of the main page. But if you have any full links, you’ll need to make sure they are written as “https://” on any SSL page.

One case where this problem can sneak in is with JavaScript snippets for services such as Google analytics. If you have a Google analytics link on your page, change it from “http://www.google-analytics.com/urchin.js” to “https://ssl.google-analytics.com/urchin.js”. (Google has now change its analytics code so this isn’t necessary if you’re using their current code.) This will work fine on all pages, whether they use SSL or not, and it will keep the analytics link from triggering the mixed content warning.

Now you have SSL, but …

Now your site can send pages in encrypted form, so they can’t be snooped on by prying eyes along the path between your server and your user’s computer. And the user can examine the certificate to see to whom it was granted, and know that the browser would warn them if the domain name didn’t match.

Of course, there’s much more to keeping your users’ data secure. You need to ensure that all model requests are properly scoped, and for internal security, you need to make sure that confidential information isn’t leaking out in your logs, exception reports, and backups.


Add Your Comments

(not published)

installing ssl on rails 3.0

From: Isaac Oren, 12/09/10 12:45 AM

I"ll appreciate some help installing ssl on rails 3, thing may have changed.. there is no plugin script in the script directory in my app folder and the command script/plugin install ssl_requirement - fails.. any idea how to install this plugin/requirements? thanks, Itzik

VHost config

From: xv22, 10/27/09 09:11 AM

You can put both VHosts into one config section: ... common config ... ... AND SSL config ... SSL is not required even if the host contains SSL config (except you set SSLRequireSSL)

form data post with SSL

From: Michael Slater, 03/31/09 10:45 PM

In my experience, as long as the action to which the form submits is included in the ssl_required list, this works. The data gets lost if the form submits to an action that is not in the ssl_required list.

Ajax form submit from non-ajax page

From: Ash, 03/31/09 10:33 PM

As Michael suggested, it doesn't work "simply by having SSL requirement". It just redirects the form POST request to SSL-GET, losing the post data. Even changing the form protocol to 'https' doesn't work as prototype doesn't send the Ajax-SSL request from non-SSL page. I'm stuck on this. Has anybody got a solution? I'm using Rails 2.1.0

Ajax form submit from non-ajax page

From: Ash, 03/31/09 10:33 PM

As Michael suggested, it doesn't work "simply by having SSL requirement". It just redirects the form POST request to SSL-GET, losing the post data. Even changing the form protocol to 'https' doesn't work as prototype doesn't send the Ajax-SSL request from non-SSL page. I'm stuck on this. Has anybody got a solution? I'm using Rails 2.1.0

Solution to SSLCertificateChainFile gives error

From: Vish, 02/19/09 10:11 AM

I found a solution to my problem mentioned earlier. The issue is with keeping the files in that directory (apache2/conf/). If I move the .crt and .key files to another directory the problem goes away. I guess apache expects all the files in the conf directory to have a syntax like the one in its config files.

SSLCertificateChainFile gives error

From: Vish, 02/19/09 09:52 AM

When I try to start the apache server (or do apache2ctl configtest), I get an error about the content of my certificate bundle file though the file seems fine to me. ------------------------------ * Starting web server apache2 Syntax error on line 1 of /etc/apache2/conf.d/gd_bundle.crt: Invalid command '-----BEGIN', perhaps misspelled or defined by a module not included in the server configuration ------------------------------ Would appreciate any pointers to resolve this. Excerpt from my config: # Server Certificate SSLCertificateFile /etc/apache2/conf.d/yyy.crt # Server Private Key SSLCertificateKeyFile /etc/apache2/conf.d/yyy.key # Server Intermediate Bundle SSLCertificateChainFile /etc/apache2/conf.d/gd_bundle.crt

Bodhost.co.uk

From: Bodhost.co.uk , 11/26/08 10:08 AM

Thanks for this informative post, by following the above article I have successfully installed SSL.

Nginx

From: Bill, 08/27/08 01:20 PM

Do you have samples or tutorials for doing the same with nginx?

Ajax and SSL

From: Michael Slater, 07/30/08 11:19 PM

I think you could do an SSL ajax request from a non-SSL page, simply by having SSL requirement set for the Ajax action (but I haven't tried it). The user wouldn't know it was secure, though, so they might not be happy if they are security conscious. You cannot do a non-SSL ajax request from an SSL page, because that breaks the security model.

Ajax Calls

From: Art, 07/30/08 05:28 PM

Is there anyway for me to do an Ajax request from a non secured page? I have an inline login form on a page that has no business being secured other than the login. Rarely do people try to login from there, and when they do, I'd like to secure it. This of course breaks with this implementation, are there any tricks?

conf.commong file example

From: Michael Slater, 07/07/08 02:29 AM

Shamika,

Here’s an example common.conf file. Obviously this depends on how you have your mongrels configured and other details of the server, as well as the name of the application, but this should give you an idea of what to expect to put in this file.

  ServedomainnamerName www.domainname.com  
    ServerAlias domainname.com

  DocumentRoot /var/www/apps/ah/current/public

  <Directory /var/www/apps/appname/current/public>
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  </Directory>

  # Configure mongrel_cluster 
  <Proxy balancer://app_cluster>   
    BalancerMember http://127.0.0.1:8000   
    BalancerMember http://127.0.0.1:8001    
  </Proxy>

  RewriteEngine On

  # Check for maintenance file and redirect all requests
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]

  # Rewrite index to check for static
  RewriteRule ^/$ /index.html [QSA] 

  # Rewrite to check for Rails cached page
  RewriteRule ^([^.]+)$ $1.html [QSA]

  # Redirect all non-static requests to cluster
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://app_cluster%{REQUEST_URI} [P,QSA,L]

  # Deflate
  AddOutputFilterByType DEFLATE text/html text/plain text/xml
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

  ErrorLog logs/domainname.com-error_log
  CustomLog logs/domainname.com-access_log combined

Help

From: Shamika, 07/06/08 10:32 PM

can u give a sample domainname.conf.common file… I just need to clarrify the configuration

Help

From: Shamika, 07/06/08 10:32 PM

can u give a sample domainname.conf.common file… I just need to clarrify the configuration

This helps

From: Allen, 06/22/08 07:36 PM

This tutorial helps me so much, many thanks!

RE: Windows XP

From: Christopher Haupt, 05/06/08 02:03 AM

Don: Most of the instructions are the same with the exception of the tools for generating your key and possibly how things go in your web server if you are using something other than Apache. Take a look at our Apache topic on BuildingWebApps.com for a couple of links to articles that might help out.

Using SSL in Rails Applications

From: Don, 05/04/08 01:14 PM

Excellent! Do you have an xp version of the instructions? I know why would anyone want it?

Very usefull article

From: Finanzamt, 04/25/08 05:48 PM

Very usefull article, in my app http://www.infakt.pl I have ssl on some pages.

 

Sponsored By

New Relic Rails Performance Monitoring