Suas aplicações rails são seguras? (mass assignment)

Posted by Leonardo Fri, 03 Aug 2007 01:27:39 GMT

Mass assignment é uma ferramenta muito poderosa que o ActiveRecord nos fornece. Com ela, podemos criar um objeto, passando todas as informações de um formulário, simplesmente com uma linha de código. Mas, sem tomar as devidas precauções, podemos nos encrencar feio criando falhas de segurança na aplicação.

Vejamos um exemplo simples de como isso pode acontecer. Suponha que tenhamos esse migration:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column :name, :string
      t.column :admin, :boolean
    end
  end

  def self.down
    drop_table :users
  end
end

esse controller:

class UserController < ApplicationController
  def create
    @user = User.create(params[:user])
  end
end

e esse view:

<form method="post" action="/user/create">
  <input type="text" name="user[name]" />
</form>

Tudo pronto e funcionando. Até que de repente, alguém resolver brincar um pouco com os dados do formulário e submete um form com os seguintes dados:

<form method="post" action="/user/create">
  <input type="text" name="user[name]" />
  <input type="hidden" name="user[admin]" value="1" />
</form>

Pronto. O usuário agora também é um administrador e pode fazer coisas desastrosas.

Podemos resolver isso dizendo para o model quais são os campos protegidos e que serão ignorados pelo mass assignment.

class User < ActiveRecord::Base
  attr_protected :admin
end

Outra forma de resolver o problema, é dizendo para o model quais são os campos permitidos para mass assignment.

class User < ActiveRecord::Base
  attr_accessible :name
end

A diferença entre as duas abordagens é, que na primeira, os campos são permitidos por default e somente os campos listados são protegidos, enquanto que na segunda acontece justamente o contrário.

Particularmente, acho a segunda abordagem mais segura, porque, se criarmos mais campos posteriormente, eles estarão protegidos por default.

A seguir, falarei de SQL Injection.

Posted in ,  | Tags ,  | no comments

Suas aplicações rails são seguras? (scoped queries)

Posted by Leonardo Tue, 31 Jul 2007 21:46:51 GMT

É verdade que utilizando todas as facilidades que o rails nos oferece, conseguimos criar aplicações de forma muito rápida. Isso é ótimo, mas às vezes, criamos uma aplicação tão rapidamente, que ou esquecemos de olhar alguns aspectos de segurança ou não sabemos o que acontece por debaixo dos panos e aí deixamos a aplicação exposta achando que tudo está funcionando redondo.

Vou escrever uma série de artigos mostrando alguns pontos que merecem uma certa atenção quando desenvolvemos nossas aplicações em ruby on rails.

Queries restritas ou scoped queries

Aplicações rails costumam usar IDs de registros nas urls, como por exemplo /contacts/show/28 ou para ser RESTful /contacts/28. Se tivéssemos construído nossa aplicação assim:

class User < ActiveRecord::Base
  has_many :contacts
end

class Contact < ActiveRecord::Base
  belongs_to :user
end

Os models são bem simples. Agora o controller:

class ContactsController < ApplicationController

  before_filter :require_signin

  def new
    @contact = Contact.new
  end

  def create
    contact = Contact.new params[:contact]
    contact.user_id = session[:user_id]
    contact.save
    redirect_to contact_url(contact)
  end

  def show
    @contact = Contact.find params[:id]
  end

  private

    def require_signin
      return false unless session[:user_id]
    end

end

Consegue ver o problema? Ele está no action show. Ainda não viu? Repare que, qualquer pessoa, estando logada no sistema, pode ver qualquer contato. Mesmo que o contato não pertença a ela. Simplesmente acessando uma url /contacts/show/id, passando o id que eu quiser, verei o contato em questão.

Para resolver isso, podemos fazer uma query com escopo definido. Como fazemos isso? Veja abaixo as modificações:

class ContactsController < ApplicationController

  # criamos o objeto @current_user
  before_filter :require_signin

  # safely looks up the contact
  before_filter :find_contact, 
          :except => [ :index, :new, :create ]

  def index
    @contacts = @current_user.contacts.find :all
  end

  def new
    @contact = @current_user.contacts.new
  end

  def create
    @current_user.contacts.create params[:contact]
    redirect_to contacts_url
  end

  def show
  end

  def edit
  end

  def update
    @contact.update_attributes params[:contact]
    redirect_to contact_url
  end

  def destroy
    @contact.destroy
    redirect_to contacts_url
  end

  private

    def require_signin
      @current_user = User.find session[:user_id]
      redirect_to(home_url) and return false 
               unless @current_user
    end

    def find_contact
      @contact = @current_user.contacts.find params[:id]
    end

end

Veja que agora, não acessamos mais o model Contact diretamente. Todas as consultas ficam restritas ao usuário logado no sistema e assim, apenas os contatos dele estarão visíveis.

O próximo artigo falará de mass assignment. Fiquem ligados.

Posted in ,  | Tags ,  | 2 comments