1.2. Creating records directly from form parameters

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.

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.