codedecoder

breaking into the unknown…

security in rails

3 Comments

Any thing on the net is vulnerable to attack. The threat can be at any layer: databaseweb server or web application. Some of the common security threat are : Cross Site Scripting (XSS), Cross Site Request Forgery (CSRF, XSRF), Man in the Middle (MITM)SQL Injection (SQLI)Mass Assignment & Parameter Injection etc . There is nothing like complete protection. There is a proverb in hindi “taala sarifon k liae hota hai…choron k liae nahi” , means “Lock on the door can prevent good peoples from entering the house not the thieves“. But at least the lock can make the thieve to struggle for entry and provide 60% security to our house.

Rails framework, provide 70% inbuilt security if you use all its feature properly and adhere to its security guideline . Web Security is a wide field in itself. I will keep updating this blog as I keep exploring, new security measure . Meanwhile If you want to explore it yourself, you can read the below resource :

=> Ruby on Rails Security Guide
=> Ruby official Security page
=> Rubyon Rails Security mailer list

Defence 1 : Protection against Mass Assignment attack.

Many a time, you have used @user = params[:user] in your code . This is a case of mass assignment i,e any thing coming in params[:user] will be assigned to object @user and when you save the object all of them get saved into database. Now here a attacker come, will modify the form in firefox console and add another input field for user with :admin => true , now when you do @user = params[:user] and save the object , he will get admin privilege and can do any thing now onward.

So simple to break in ..right ??

Not so easy with rails 3 and upper version. Now attributes are prevented from mass assignment by default. So you have to explicitly define the list of attributes which are eligible for mass assignment with attr_protected, or attr_accessible .

attr_accessible :name, :age # it is white listing of attributes for mass assignment

It means only name and age can be mass assigned, if params[:user] cantain any attributes say :admin => true , it will throw error.

attr_protected :admin # it is blacklisting of attributes for mass assignment

It means any attribute other then :admin can be mass assigned.

I suggest you to use attr_accessible approach. I feel it is more safe. say you add a new attribute salary in the user table but forgot to modify your model, it will automatically become eligible for mass assignment with attr_protected approach, but with attr_accessible approach it will throw error. Now if you feel salary safe to be mass assigned, you can add it to attr_accessible list.

 

Defence 2 : protection against cross site scripting (XSS) .

Oh..! You haven’t heard about cross site scripting (XSS) …Read it here . In short, It is execution of script by attacker on your page which may stole cookies session or any other information or break your application.

Let us see a simple example :

let you have a application in which person1 , input a message for person2, which get stored in db,when person2 open his account the message displayed on his page with the code <%= @message %> . But person1 is a bad Guy(remember, mom use to remind us – world is full of bad people) . He entered the message “<script>alert(‘Iam virus, Iam in..I will break your code’)</script>” .

When person2 open his page, what he will see. He will see the message “<script>alert(‘Iam virus, Iam in..I will break your code’)</script>” . No harm occur. Person1 must be cursing Rails. With Rails3 any HTML character in input will automatically escaped, in older version you have to use h() explicitly i,e the input code is <%=h text_field_tag “message” %> . More detail on escaping HTML is available here . So the ill conceived message of person1 get passed to db in harmless form as “&lt;script&gt;alert(‘Iam virus, Iam in..I will break your code’)&lt;/script&gt;” .

Now say, person1 is big crook and able to insert the message “<script>alert(‘Iam virus, Iam in..I will break your code’)</script>” in db. But person2 is again not harmed, as not only input but Rails escape any HTML in the ruby code. But what if you do not want to escape the html tag in the message. You want the message to be displayed as such, because Person3 is a nice person and he send a well decorated message “Arun <strong>I Love You</strong>” , in which I love you is highlighted . You want it to display as “Arun I Love You, rather than “Arun <strong>I Love You</strong>” . So you need to override the Rails default escaping behaviour. below will do that

<%=raw  @message %>

great, person3 message look great, but what is this person1 ill conceived message also get executed, you see the alert box below

xss example

xss example

So, person2 has succeeded in attacking your application . using raw for rendering is risky…will you use that???…I don’t think so . If you still want bolded I Love You, try other alternative than raw : sanitize .

 

 Defence 3 : Protection against CROSS SITE REQUEST FORGERY(CSRF) .

A good example of CSRF is available here . The detail documentation of preventing CSRF attack is available here . So it is more like attack through chat messages, email, images etc. This attack is mostly executed on Authenticated i,e logedIn users. Basically, the cross site scripting attack happen due to your trust on the website, which is not fulfilled while the Cross Site request forgery attack occur due to website trust on you. Website trust you with using it properly, identifying its purpose, content, flow etc. It do not expect you to click on the link like “HOT babes”, “Free tour package”, “your own FB profile link with friend request from a sweet girl” etc. If they are not part of the website on your earlier website, there is great chance that some one attempting CSRF attack.But we are INNOCENT people, we are oblivion of any such attack. Rails come to your rescue.

If you are using rails version 3 or above, you can see below within head tag of layout generated by default.

<%= csrf_meta_tags %>

This line will create a secret token for each user session. If you see the page source, you can see the below line generated by the above code.

<meta content=”sptmtpH+EZYnhrfwPplCQkATltqeHwaBAOJAgyj8CII=” name=”csrf-token” />

The above token value is stored in a hidden filed within each form. It look as below in one of my form for the current session.

<input type=”hidden” value=”sptmtpH+EZYnhrfwPplCQkATltqeHwaBAOJAgyj8CII=” name=”authenticity_token”>

When, my session expire and I log in again, this token will change. attacker can never know this secret as it is generated on fly.So attacker can disguise you to submit a form which look like a form of your web application, but he can’t bypass the authenticity token and rail will throw an error, saving us from the attack.

You can bypass the above default security measure of rails with below line of code, if you find any reason to do so.

 skip_before_filter :verify_authenticity_token

But it is like “Going to war without a shield” .will you do that…??.. NO…I know you are mature enough.

Also, always submit form with post method. Get method is too easy to attack. Post is also not fool proof, but is harder to attack.

match “/launch_all_the_missiles” => “missiles#launch_all”, :method => :get

look crazy…look like any one can destroy the world. post provide better protection against attack. If you want such route in your application write it as post method

match “/launch_all_the_missiles” => “missiles#launch_all”, :method => :post

 

Defence 4 : protection against Packet Sniffing (MITM).

Packet Sniffing is also known as Man IN The Middle attack. It is same as, you written all your secret in a letter , posted it to your lover, but postman read it before delivering. There are many software like FireShip , which  can track the bits crossing the wire, and if the data is in plain text..your game is over. Isn’t good to write the letter in encrypted word before posting, the postman would not have know the secret even if he manage to open the letter. Rails do that encryption for you if you set below configuration in your environment file.

config.force_ssl = true
It will Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. Now data will be send in encrypted form.

 

 

Defence 5: protection against SQL injection
This is one of the most common and abused attack on any web application, and involve hijacking sql command. let us take a example of a banking application, which allow a loged in user to see all the loan he has applied for. When a user click on any of the listed loan, your application show the details of that loan. Let the user is genuine one, who do not alter the params. let params[loan_id] = “23239”

Being a Rails developer, you may use where clause of Active::Record to retrieve the detail. But, you can construct a where clause in three ways as below.

1.9.3-p194 :021 > @loan = Loan.where(:loan_id => params[:loan_id])
Loan Load (0.1ms)  SELECT `loans`.* FROM `loans` WHERE (loan_id = ‘23239’ )
=> [#<Loan id: 376, user_id: 41, loan_number: “100150”, status: “Declined”, app_name: “Pisa Productions”, app_date: “2014-11-24 08:00:00”, relationship_manager: “”, primary_borrower: nil, owner: nil, guarantor_1: nil, guarantor_2: nil, product: nil, loan_amt: 100000, business_type: nil, finance_rate: nil, payment_amt: nil, years_in_business: nil, terms: nil, created_at: “2014-01-02 11:06:08”, updated_at: “2014-01-02 11:06:08”, sales_force_application_id: “006d000000Gj5n5AAB”, loan_id: “23239”, dealership_name: “Pisa Productions”>]

1.9.3-p194 :020 > @loan = Loan.where(“loan_id = ? ” ,params[:loan_id])
Loan Load (0.4ms)  SELECT `loans`.* FROM `loans` WHERE (loan_id = ‘23239’ )
=> [#<Loan id: 376, user_id: 41, loan_number: “100150”, status: “Declined”, app_name: “Pisa Productions”, app_date: “2014-11-24 08:00:00”, relationship_manager: “”, primary_borrower: nil, owner: nil, guarantor_1: nil, guarantor_2: nil, product: nil, loan_amt: 100000, business_type: nil, finance_rate: nil, payment_amt: nil, years_in_business: nil, terms: nil, created_at: “2014-01-02 11:06:08”, updated_at: “2014-01-02 11:06:08”, sales_force_application_id: “006d000000Gj5n5AAB”, loan_id: “23239”, dealership_name: “Pisa Productions”>]

1.9.3-p194 :022 > @loan = Loan.where(“loan_id = ‘#{params[:loan_id]}’ “)
Loan Load (0.2ms)  SELECT `loans`.* FROM `loans` WHERE (loan_id = ‘23239’ )
=> [#<Loan id: 376, user_id: 41, loan_number: “100150”, status: “Declined”, app_name: “Pisa Productions”, app_date: “2014-11-24 08:00:00”, relationship_manager: “”, primary_borrower: nil, owner: nil, guarantor_1: nil, guarantor_2: nil, product: nil, loan_amt: 100000, business_type: nil, finance_rate: nil, payment_amt: nil, years_in_business: nil, terms: nil, created_at: “2014-01-02 11:06:08”, updated_at: “2014-01-02 11:06:08”, sales_force_application_id: “006d000000Gj5n5AAB”, loan_id: “23239”, dealership_name: “Pisa Productions”>]

So, the sql query generated by Active::Record for the different syntax of where clause is same. But under the hood, the third form of where clause is vulnerable to sql attack but not the first two. Now say the bad guy enter, and he want to know the loan detail of any other person. He will just change the params[:loan id] to make a sql attack. This will do the trick for him.

params[:loan_id] = “‘ OR 1=1–‘”

The above is a sql injection code, you can easily see that, it will always return true as even if all other condition return false 1=1 will definitely going to return true true making the OR to return true at the end.

Again run the above in console. You will get below result.

1.9.3-p194 :024 > @loan = Loan.where(:loan_id => params[:loan_id])
Loan Load (0.3ms)  SELECT `loans`.* FROM `loans` WHERE `loans`.`loan_id` = ‘\’ OR 1=1–\”
=> []
1.9.3-p194 :025 > @loan = Loan.where(“loan_id = ? ” ,params[:loan_id])
Loan Load (0.4ms)  SELECT `loans`.* FROM `loans` WHERE (loan_id = ‘\’ OR 1=1–\” )
=> []
1.9.3-p194 :026 > @loan = Loan.where(“loan_id = ‘#{params[:loan_id]}’ “)
Loan Load (0.3ms)  SELECT `loans`.* FROM `loans` WHERE (loan_id = ” OR 1=1–” )
=>  => [#<Loan id: 376, user_id: 41, loan_number: “100150”, status: “Declined”, app_name: “Pisa Productions”, app_date: “2014-11-24 08:00:00”, relationship_manager: “”, primary_borrower: nil, owner: nil, guarantor_1: nil, guarantor_2: nil, product: nil, loan_amt: 100000, business_type: nil, finance_rate: nil, payment_amt: nil, years_in_business: nil, terms: nil, created_at: “2014-01-02 11:06:08”, updated_at: “2014-01-02 11:06:08”, sales_force_application_id: “006d000000Gj5n5AAB”, loan_id: “23239”, dealership_name: “Pisa Productions”>,
..all othe records]

So you can see that, the first two syntax escape the special character while generating the sql query, thus preventing from sql injection but the last one do not.

Always, be careful while writing your where clause.

Take another example, this is serious one, with your negligence any one can bypass your authentication system. You are stubborn to my advice and goes with your old way of writing the where clause.

User.where(“email = ‘#{params[:email]}’ AND encrypted_password = ‘#{params[:password]}'”)

On your login page the user just need to enter ‘ OR 1=1–‘ in both email and password field, and he will be loged in.

But Rails also not provide full protection against SQL injection, may be they get corrected in upcoming version, but currently, like where, if you not use properly all the following methods are vulnerable to sql injection( READ THE DETAIL HERE) : average ,calculate ,count , maximum ,minimum , sum, delete_all, exists?, find, find_by,join, pluck etc

 

 

REFERENCE :

http://rails-sqli.org/

http://guides.rubyonrails.org/security.html

http://blog.nvisium.com/2014/09/understanding-protectfromforgery.html

 

 

Author: arunyadav4u

over 7 years experience in web development with Ruby on Rails.Involved in all stage of development lifecycle : requirement gathering, planing, coding, deployment & Knowledge transfer. I can adept to any situation, mixup very easily with people & can be a great friend.

3 thoughts on “security in rails

  1. Reblogged this on bob-roberts.net and commented:
    Great info here for securing Rails site. Did not know of sanitize but I am going to use it. Thanks 🙂

  2. how about strong parameters?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s