Key Takeaways
- Rails provides a robust framework for building secure websites, but the actual implementation is down to the developer. It is crucial not to trust data implicitly, to ensure your version of Rails is up-to-date, and to understand that you can never fully guarantee your site’s security.
- Session hijacking, where a hacker intercepts and takes over a user’s authenticated session, can be prevented by encrypting your entire site behind https. This requires purchasing an SSL certificate, setting up your web server to support SSL, and ensuring all assets and links are https.
- Session fixation attacks, where a hacker sets up a session and then overrides a real user’s cookie to share the same session id, can be prevented by destroying the session and giving the user a new session when they log in.
- Cross-Site Request Forgery (CSRF) attacks, where a third-party redirects a user to a destructive URL, can be prevented by Rails creating a per-session “authenticity_token”. This token is required for every non-GET request to the server, and Rails automatically inserts this token into every form generated by form_for or form_tag.
- Using RESTful routes can further enhance security. If you create a method with changing or destructive properties that can be accessed via a GET request, your application is open to CSRF attacks. Using RESTful routes, Rails’ protect_from_forgery will protect against CSRF when the PUT update is sent.
-
- During the seven years I’ve been using Rails, the framework has exploded in popularity because it’s made creating powerful applications quick and easy. It seamlessly handles so many of the issues that used to absorb developers’ time, such as database integration, session handling, template render, etc. Rails does it all for us.
-
-
- In this article, I explain some ways that hackers can manipulate data before it even reaches your application, how Rails protects us from this manipulation, and what you can do to protect yourself further.
- Next, we’ll dig into Rails a bit more and look at the methods that you need to use to protect yourself once the data actually gets into your application.
- Finally, we’ll look at rendering that data, and other Rails security issues you need to be aware of.
-
It’s all about the data!
Websites are all about data. The data comes in, it’s stored, and then it’s rendered out to the end-user. The worst mistake you can make when building a website is to trust that data. Don’t trust where it’s come from; don’t trust what it is; and don’t trust what it’ll look like when you render it. Whether you’re building a personal blog, or a big e-commerce site, the moment you start to gain popularity, someone is going to try and take advantage of you, and they’ll most likely do it by manipulating the data your site is using. Before we start looking at how Rails deals with these issues, check your version of Rails is up to date. Rails is a library that you are using to build your website. In whatever sphere of development you work in, it is essential that you use the latest versions of libraries, as they often provide important security updates. This article presumes that you are using at least Rails 3.2.5. If you’re not, then you should consider upgrading immediately.When is a user not a user?
You have no way of knowing who or where the data that hits your application is coming from. This is a fundamental principle of building a website. Authentication adds a layer of security, but you still have no guarantee that the requests hitting your site are from the authenticated user. There are many ways that hackers can masquerade as users and it is your responsibility to stop them from doing so.Session Hijacking
I’m writing this while sitting in a coffee shop. I could start spying on the network traffic of everyone else in here in minutes. If your website’s url starts with “http://” and someone is logging into your website, I could intercept the login request and see their passwords in clear text. The only way to stop me from doing so is to use HTTP Secure on that login page, which will encrypt the traffic between the user and your website. If I snoop the packets then, I’ll see the data being sent, but I won’t be able to make any sense of it. This protects the users’ credentials, and is a good start, but it still doesn’t protect the user’s session. I could simply wait until our susceptible coffee-drinker has logged in to your site, and then intercept and hijack their authenticated session, giving me access to their account. It is, therefore, absolutely essential to encrypt the whole of your site behind https. A few years ago this was discouraged because https was ‘expensive’ in processing time, caching and more, but now it has now become the accepted wisdom that security matters more than speed. In fact, with modern technology, the extra time the encryption takes is actually very small. On my sites, I tend to see a 20ms increase in page load time. I’m happy with that tradeoff for my users’ security. So how do you implement this in Rails? There are three things you need to do. Firstly, you need to purchase an SSL certificate and setup your web server to support SSL. If you’re using Heroku, herokuapp sub-domains now have SSL by default. Setting up SSL on your own domain is easy and basically entails two commands: [gist id=”2882647″] Secondly, you need to tell your Rails app to only run on https. Rails makes it easy. Simply add the following in your config block inconfig/application.rb
.
[gist id=”2882634″]
Finally, you need to make sure that all your assets and links are https. If you have http assets on an https page, the user’s browser will display a mixed-content warning in the browser bar. As normal, Rails does most of the work for you, but if you have any hard-coded “http://” internal-links or images, make sure you change them. If you have any hosted services that you link to (e.g. Typekit, external javascript libraries, external asset management through S3
, etc), make sure those links are https too.
One final thought with regards to session hijacking: make sure your sessions expire in a reasonable time and that you give users the option to log out. If people are using shared or public computers, the last thing you want is a malicious person to come along five minutes after your user has left and make use of their account because the session is still active.
Storing Data in a Session
There are various session store types that you can use in Rails. Since v2, the default has been to store the session in a cookie. This is fast and scales well, but it does mean that you have to be very careful about the data that you store for two reasons. Firstly, cookies are limited to 4kb and so if you store too much in them, you will lose data. Secondly, cookies are stored on the client-side and therefore the data stored within the session can be viewed. Rails includes a SHA512 signature hash (seeded with a secret string stored on the server) to stop the data from being tampered with, but it has no way of stopping the data from being viewed. This means that if you decide to store sensitive data in the session, you are basically exposing it to the world. For 99.99% of applications, all you need to store in a session is the user’s id. This is perfect for cookies and why cookie storage is Rails’ default. If you have a real, solid reason to store other data in the session (and you really have to question whether that the reason is a good enough one), take the safe route and store the session data in the database. You can make the change it inconfig/initializers/session_store.rb
:
[gist id=”2882648″]
You will need to generate the session database as well:
[gist id=”2882644″]
Session Fixation Attacks
Session fixation attacks are rare but deadly. They involve a hacker setting up a session (normally achieved by just visiting as site) and then overriding a real user’s cookie so that they both share that same session id. Once the real user has authenticated (and therefore that session is authenticated), the hacker can access the site as if he is the real user. The Rails security guide explains this in more detail. Protecting against these attacks is easy. If you are using sessions to simply store the user_id for authentication purposes, then you can simply destroy the session and give the user a new session when they log in. This stops the hacker from sharing a session with an authenticated user. To achieve this, simply add this to your sessions controller: [gist id=”2882651″] Any authentication gems should do this for you, but it’s essential that you check yours does. The flavour of the moment, Devise, has protected against session fixation attacks since version 1.1.4.Cross-Site Request Forgery (CSRF) Attacks
CSRF attacks have been witnessed in the wild since the turn of the century and are incredibly dangerous. Rails does a lot of work to protect against these attacks. However, it’s important to understand how the attacks work and make sure you don’t weaken your application to them. A CSRF attack occurs when a third-party redirects the user to a destructive URL. An example:-
-
- On Twitter, users can update their profiles with a PUT request to
http://www.twitter.com/settings/profile
. - I place a link on my website such as:
<a href="http://www.twitter.com/settings/profile?_method=put&email=l33t@hackers.com">Win a million pounds<a>
. - The user logs into Twitter, reads his latest tweets and leaves.
- He then visits my site, clicks on my special link and in doing so, updates his email address on Twitter.
- I now head to twitter, click on the “forgotten password” link, specifying “l33t@hackers.com” as the email, recieve a new password in my inbox, and obtain access to the users account.
- On Twitter, users can update their profiles with a PUT request to
-
form_for
or form_tag
, onto any links that have a method: "POST"
, etc, and into Javascript callbacks using the jquery_ujs asset that is automatically inserted into your application.js. If you want to see an example of this, view the source of the Twitter page in my example to see it on a live site. By default, in your application_controller, there is a single line containing: protect_from_forgery
. This is what tells Rails to check for that token on every non-GET request. In only the most unusual circumstances should you remove this line of code. Doing so would render your whole application vulnerable to CSRF attacks.
So if Rails does all this for you, what do you have to do yourself? Well, there are two things to make sure do. Firstly, you should ensure that if you use any external Javascript libraries that do not use jQuery for their AJAX callbacks, that you manually insert the authenticity_token. The token’s name and value are stored in meta tags for you, so you can craft a URL as follows:
[gist id=”2882640″]
The second thing is more complicated and deserves a section to itself.
Use RESTful Routes
The Rails API gives us a valuable piece of information aboutprotect_from_forgery
:
Bear in mind that only non-GET, HTML/JavaScript requests are checked.If you create a method that has any changing, or destructive properties, and it can be accessed via a GET request, you are heading for trouble. Let’s say we are creating our own settings functionality. We have a controller and routes file as follows: [gist id=”2882652″] [gist id=”2882668″] Your application is now totally open to CSRF attacks. I can redirect a user to
/update_settings?email=l33t@hackers.com
and the email address will be changed. If you use match
in your routes, then your code may well be vulnerable. This is such a big issue, that match
is going to be removed from Rails 4, instead requiring you to specify the methods as per:
[gist id=”2882684″]
That’s better, but we can do better still. We can use RESTful routes, Rails style. If we treat settings as resource, we can specify the same URL for both routes, and have the functionality determined by the method. This is how Twitter’s settings/profile
page, which we mentioned above, works. Doing this in your routes.rb is easy. Just tell rails you’re working with a resource:
[gist id=”2882686″]
We now have one url: /settings
, which we can use to view or update our settings, using GET or PUT. Rails’ protect_from_forgery
will kick to protect us against CSRF when the PUT update is sent.
Trust No-one!
By following the advice above, you will protect your users from the majority of known threats, and help guarantee that your requests are coming from an expected source. However, it’s essential to remember that even if you follow all this advice, your site will still be vulnerable. The hackers are always looking for the next step clever technique to take advantage of your site, and we are always playing catchup in defending ourselves. In the next article, we’ll dig into Rails a bit more and look at the methods that you need to use to protect yourself once the data actually gets into your application. In the third part, we’ll look at rendering that data, and other Rails security issues you need to be aware of. In the mean time, start securing your application…Frequently Asked Questions on Securing Your Website with Ruby on Rails
What are the basic security measures I should implement in Ruby on Rails?
The basic security measures in Ruby on Rails include data validation, session management, and error handling. Data validation ensures that only valid data is processed by your application. Session management helps to protect sensitive data from unauthorized access. Error handling prevents the exposure of sensitive information through error messages. Additionally, you should also use HTTPS to encrypt data in transit and regularly update your Ruby on Rails framework to benefit from the latest security patches.
How can I use HTTPS with Ruby on Rails?
HTTPS can be implemented in Ruby on Rails by acquiring an SSL certificate and configuring your server to use it. This process involves generating a certificate signing request (CSR), purchasing an SSL certificate from a trusted certificate authority (CA), and installing the certificate on your server. Once installed, you can configure your Ruby on Rails application to force all traffic to use HTTPS.
What is the importance of updating Ruby on Rails for security?
Regularly updating Ruby on Rails is crucial for security because each new version comes with security patches that fix known vulnerabilities. By not updating, you leave your application exposed to these vulnerabilities, which can be exploited by attackers. Therefore, it’s recommended to always use the latest stable version of Ruby on Rails.
How can I secure my local development environment in Ruby on Rails?
You can secure your local development environment in Ruby on Rails by using a self-signed SSL certificate. This certificate encrypts data in transit between your local server and your browser, protecting it from eavesdropping. However, because self-signed certificates are not trusted by browsers, you will need to configure your browser to accept it.
What are the security features provided by Ruby on Rails?
Ruby on Rails provides several security features out of the box. These include CSRF protection, XSS protection, SQL injection protection, and secure cookies. CSRF protection prevents cross-site request forgery attacks, XSS protection prevents cross-site scripting attacks, SQL injection protection prevents SQL injection attacks, and secure cookies prevent session hijacking.
How can I protect my Ruby on Rails application from SQL injection attacks?
Ruby on Rails provides built-in protection against SQL injection attacks. This protection is achieved by using parameterized queries, which separate SQL commands from data, preventing attackers from injecting malicious SQL commands. However, you should still validate and sanitize user input to further enhance your application’s security.
How can I protect my Ruby on Rails application from XSS attacks?
Ruby on Rails provides built-in protection against XSS attacks by automatically escaping HTML output. This prevents attackers from injecting malicious scripts into your web pages. However, you should still sanitize user input and avoid using methods that bypass HTML escaping.
How can I protect my Ruby on Rails application from CSRF attacks?
Ruby on Rails provides built-in protection against CSRF attacks by including a security token in every form. This token is verified by the server when the form is submitted, preventing attackers from forging requests. However, you should still use HTTPS to protect the security token from being intercepted.
How can I secure cookies in Ruby on Rails?
You can secure cookies in Ruby on Rails by setting the secure and httponly flags. The secure flag ensures that cookies are only sent over HTTPS, protecting them from eavesdropping. The httponly flag prevents cookies from being accessed by JavaScript, protecting them from XSS attacks.
How can I handle errors securely in Ruby on Rails?
You can handle errors securely in Ruby on Rails by configuring your application to not reveal sensitive information through error messages. This can be achieved by using custom error pages and logging errors for review. Additionally, you should also handle exceptions properly to prevent unexpected behavior.
Jeremy Walker has been using Rails since 2005. He is the CTO of Meducation - an educational social-network for medics - and runs his own software consultancy. He is a maintainer of various open source projects, including Propono, Larva and Inqusitio. You can follow Jeremy on Github and Twitter, and read more about him at his website.