Use attr_protected or we will hack you

Written by on Mar 11 2008

A good friend of mine recently asked me to look at his open source project and tell me what I think. While looking through the very nice code I discovered a security hole and promptly created a user account with administrative privileges using one command. Here is the command: curl -d "user[login]=hacked&user[is_admin]=true&user[password]=password&user[password_confirmation]=password&user[email]=hacked@by.me" http://url_not_shown/users That's right, that's all it took. Try it. Take this line and point it at your favorite website and see if you can create an admin account. There are a few easy things anyone can do to prevent this hack. In order of importance: # Use attr_protected. # Don't use mass assignments for your users table. # Don't have a users controller. # Split the users table into a users table and a profiles/people table. At Less we do all four. h2. Use attr_protected The attr_protected method in Rails will prevent the fields from being assigned via mass assignment. Here is an example: Bad:

class User < ActiveRecord::Base
        #no attr_protected here
        #this will allow the creation of your hacked admin user
        end
        
        class Users < ApplicationController
          def create
              @user = User.create params[:user]
          end
        end
        

Good:

#this will not allow the creation of your hacked admin user
        class User < ActiveRecord::Base
          attr_protected :is_admin
        end
        
        class Users < ApplicationController
          def create
              @user = User.create params[:user]
        #user is created, but the is_admin flag is not set
          end
        end
        

Wasn't that easy? You should all do this. h2. Don't use mass assignments for your users table No where in your code should you allow the users table to use mass assignments. What this means is that when a user account is created, assign each field individually. Here is an example: Bad:

class Users < ApplicationController
          def create
              @user = User.create params[:user]
          end
        end
        

Good:

class Users < ApplicationController
          def create
              @user = User.new
              @user.login = params[:user][:login]
              @user.password = params[:user][:password]
              @user.password_confirmation = params[:user][:password_confirmation]
              @user.save
        #user is created, but the is_admin flag is not set
          end
        end
        

I know what you're thinking "Steve, that is too many lines of code, can't we use the built in stuff that Rails gives us to make thing easy?" My wife's cousin is an Assistant Warden at a prison in Texas. While taking a tour I noticed a sign that says "Security is not convenient." This is also true for security code. It is more lines of code, it is harder to write, it should fail securely. If you need to have an admin screen where admins set is_admin on users, then that action should also not use mass assignment and it should be protected so that only users who are already is_admin can access it. h2. Don't have a users controller This one is stupid. Obfuscation is the worst form of security, but combined with the others it is not too bad and worth doing. This will prevent all the script kiddies I just created from attacking your site because they will not know the name of the controller to hit. Of course if they just looked at the url that your signup form submits to they will know. That is why this is the weakest form of security. In fact it's so weak it's not really even security. h2. Split the users table into a users table and a profiles/people table Keep user settings that the system needs in the untouchable users table and user configurable settings in a separate table called profiles or people or something. Doing this means the only place the users table gets written to is on account creation and if you have an admin screen. The fewer access points you have, the safer you are. It also means that for profile data, you can use your profiles controller which allows mass assignment and keep the code nice and tight. Why are you still reading? Go, fix your code right now. My spider is already loose, looking for your rails app. Go fix it already!!

Meet
Steven

Hi I'm Steven,

I wrote the article you're reading... I lead the developers, write music, used to race motorcycles, and help clients find the right features to build on their product.

Get Blog Updates

--> --> -->