Securing Rails Application
Web application security is a central component of any web-based business. The global nature of the Internet exposes web properties to attack from different locations and various levels of scale and complexity. Web application security deals specifically with the security surrounding websites, web applications and web services such as APIs.
Followings are common web app security vulnerabilities:
1) SQL Injection:
SQL injection is a web application vulnerability that occurs when untrusted data is inserted in a SQL queery without any sanitization or escaping.
If we simply pass user_input directly to this method, this would create a very basic type of SQL Injection because when a string is passed, it will be added to the query as a SQL fragment and providing such an opportunity will make attackers really happy! Let’s illustrate the point with an example:
Employee.where(“name = ‘#{params[:name]’”)
If this particular line of code invoked with name = ‘fff’, the resulting query will be:
SELECT “employee”.* FROM “employee” WHERE (name = ‘fff’)
=> #<ActiveRecord::Relation []>
But if it is set to “‘ OR 1=’1”:
SELECT “employee”.* FROM “employee” WHERE (name = ‘ ‘ OR ‘1’=’1')
=> #<ActiveRecord::Relation [#<Employee id: 1, name:’ajit’, …….>]>
As seen above, the attack is the successful inclusion of an OR operator which helped us return all the records from the database. More advanced queries could easily be crafted from this point onward.
Preventing SQL Injection:
a) When finding/retrieving information from the database, dynamic attribute-based finders should be used to avoid SQL Injection vulnerabilities.
Employee.find_by_name(name) # dynamic finder
b) Never pass a string as an argument unless it is dynamic finder.
Employee.where([“name = ?”, “#{params[:name]}”])
SQL Injection is not possible when using the above because the first element of the array is a template and the latter are parameters to that template.
Employee.where({ name: params[:name] })
Again, this is free from SQL Injection, because the column name is set explicitly to the ‘name’ and the external input set to the value of it.
2) CSRF: Cross-Site Request Forgery
CSRF stands for Cross-site request forgery. It is a technique hackers use to hack into a web application.
CSRF is a method of attack that “works by including malicious code or a link in a page that accesses a web application the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands.”
The Rails Guides has an excellent example scenario:
1) Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob’s project management application, rather than an image file: <img src=”http://www.webapp.com/project/1/destroy">
2) Bob’s session at www.webapp.com is still alive, because he didn’t log out a few minutes ago.
3) By viewing the post, the browser finds an image tag. It tries to load the suspected image from www.webapp.com. As explained before, it will also send along the cookie with the valid session ID.
4) The web application at www.webapp.com verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image.
5) Bob doesn’t notice the attack — but a few days later he finds out that project number one is gone.
Prevention
In order to prevent such things from happening Rails uses authenticity_token. If you look at source code of any form generated by Rails you will see that form contains following code.
<input name=”authenticity_token”
type=”hidden”
value=”LhT7dqqRByvOhJJ56BsPb7jJ2p24hxNu6ZuJA+8l+YA=” />
The exact value of the authenticity_token will be different for you. When form is submitted then authentication_token is submitted and Rails checks the authenticity_token and only when it is verified the request is passed along for further processing.
There are two components to CSRF. First, a unique token is embedded in your site’s HTML. That same token is also stored in the session cookie. When a user makes a POST request, the CSRF token from the HTML gets sent with that request. Rails compares the token from the page with the token from the session cookie to ensure they match.
To protect against all other forged requests, Rails introduce a required security token that our site knows but other sites don’t know. We include the security token in requests and verify it on the server. This is done automatically when config.action_controller.default_protect_from_forgery is set to true, which is the default for newly created Rails applications. You can also do it manually by adding the following to your application controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
end
This will include a security token in all forms and Ajax requests generated by Rails. If the security token doesn’t match what was expected, an exception will be thrown.
3) File Uploads:
File upload is becoming a more and more essential part of any application, where the user is able to upload their photo, their CV, or a video showcasing a project they are working on. The application should be able to fend off bogus and malicious files in a way to keep the application and the users safe.
In short, the following principles should be followed to reach a secure file upload implementation:
1) List allowed extensions. Only allow safe and critical extensions for business functionality
2) Ensure that input validation is applied before validating the extensions.
3) Validate the file type, don’t trust the Content-Type header as it can be spoofed
The Content-Type for uploaded files is provided by the user, and as such cannot be trusted, as it is trivial to spoof. Although it should not be relied upon for security, it provides a quick check to prevent users from unintentionally uploading files with the incorrect type. Other than defining the extension of the uploaded file, its MIME-type can be checked for a quick protection against simple file upload attacks.
4) Change the filename to something generated by the application
5) Set a filename length limit. Restrict the allowed characters if possible
6) Set a file size limit
7) Only allow authorised users to upload files
8) Store the files on a different server. If that’s not possible, store them outside of the webroot
In the case of public access to the files, use a handler that gets mapped to filenames inside the application (someid -> file.ext)
9) Run the file through an antivirus or a sandbox if available to validate that it doesn’t contain malicious data
10) Ensure that any libraries used are securely configured and kept up to date
11) Protect the file upload from CSRF attacks