1.2. Creating records directly from form parameters
1.1 The problem
Let’s say you want to make a user registration system. Your users table looks like this:
CREATE TABLE users ( id INTEGER PRIMARY KEY, name VARCHAR(20) NOT NULL, -- the login name password VARCHAR(20) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT "User", -- e.g. "Admin", "Moderator, "User" approved INTEGER NOT NULL DEFAULT 0 -- is the registered accound approved by the administrator? ); CREATE UNIQUE INDEX users_name_unique ON users(name);The registration form:
<form method="post" action="http://website.domain/user/register"> <input type="text" name="user[name]" /> <input type="text" name="user[password]" /> </form>The easiest way to create a user object from the form data in the controller is:
User.create(@params['user'])
But what happens if someone decides to save the registration form to his disk and play around with adding a few fields?
<form method="post" action="http://website.domain/user/register"> <input type="text" name="user[name]" /> <input type="text" name="user[password]" /> <input type="text" name="user[role]" value="Admin" /> <input type="text" name="user[approved]" value="1" /> </form>
He can create an account, make himself admin and approve his own account with one click.
1.2 The solution
Active Record provides two ways of securing sensitive attributes from being overwritten by malicious users that change the form. The first is attr_protected that denies mass-assignment the right to change the named parameters.
Using attr_protected, we can secure the User models like this:
class User < ActiveRecord::Base attr_protected :approved, :role end
This will ensure that on doing User.create(@params['user']) both @params['user']['approved'] and @params['user']['role'] will be ignored. You’ll have to manually set them like this:
user = User.new(@params['user']) user.approved = sanitize_properly(@params['user']['approved']) user. role = sanitize_properly(@params['user']['role'])
Allowing instead of protecting
If you’re afraid you might forget to apply attr_protected to the right attributes before making your model available to the cruel world, you can also specify the protection in reverse. You simply allow access instead of deny it, so only the attributes named in attr_accessible are available for mass-assignment.
Using attr_accessible, we can secure the User models like this:
class User < ActiveRecord::Base attr_accessible :name, :password end
This description has exactly the same affect as doing attr_protected :approved, :role, but when you add new attrbutes to the User model, they’ll be protected by default.