Ao utilizar o scaffold do Rails, ele criará todos os métodos necessários no controller. Depois que o usuário preenche os dados do formulário e envia, é executado o método create do controller. Este método faz algo semelhante ao código abaixo (no exemplo é um CRUD de usuário):
@usuario = Usuario.new(params[:usuario])
Na forma em que o Rails cria o formulário, os atributos do usuário são passados ao controller como um hash. O valor do params acima é algo parecido com isso:
{ "authenticity_token" => "xI1Cy+LvUZzg6FR/1Y/JHcaHVPRyWsHmRII8BhMOr0E=",
"utf8" => "?",
"action" => "create",
"controller" => "usuarios",
"usuario" => { "nome" => "Novo usuario", "email" => "[email protected]" } }
Além da definição do token de segurança, codificação em utf-8, nome do controller e da action que será executada, o parâmetro usuario contém todos os atributos que foram preenchidos no formulário. Desta forma, é muito simples atribuir os parâmetros preenchidos a um objeto Usuario, seja criando um novo (@usuario = Usuario.new(params[:usuario])
) ou editando (@usuario.update_params(params[:usuario])
). O Rails chama isso de mass assignment.
O problema desta abordagem é que o usuário poderia facilmente inserir novos parâmetros neste hash, simplesmente adicionando tags input hidden no formulário (usando o Firebug, por exemplo):
<input type="hidden" name="usuario[admin]" id="usuario_admin" value="true" />
Adicionando o código acima, o novo usuário criado receberia o valor true
no atributo admin
, o que representa uma falha grave na segurança da aplicação.
O Rails oferece um mecanismo para garantir a segurança nestes casos, usando os métodos attr_protected
e attr_accessible
do ActiveModel. O primeiro permite definir atributos que não podem ser alterados através de mass assignment:
class Usuario
attr_protected :admin
end
E o attr_accessible
é uma forma mais segura: somente os atributos passados para este método poderão ser alterados com mass assignment. Os demais ficam protegidos:
class Usuario
attr_accessible :nome, :email
end
Obviamente estes dois métodos não podem ser usados simultaneamente, pois um exclui o outro.
Se você quiser atualizar um objeto com mass assignment ignorando a segurança fornecida pelo attr_accessible
e pelo attr_protected
, basta utilizar o parâmetro without_protection
(somente no Rails 3.1):
Usuario.create(
{:nome => "Novo usuario", :email =>"[email protected]", :admin => true},
:without_protection => true)
No exemplo acima, o usuário criado terá o atributo admin
igual a true
, mesmo que tenha sido usado o método attr_protected
ou o attr_accessible
para evitar a alteração deste atributo.
Outra opção interessante para evitar a alteração de atributos indesejados é o método attr_readonly
. Os atributos passados para este método só poderão ser definidos na criação do objeto, e não poderão ser alterados depois. Porém, este método faz parte do ActiveRecord::Base, e não do ActiveModel, ou seja, ele não estará disponível se você usar outro ORM. Há uma issue aberta no Github solicitando que este método seja movido para o ActiveModel.
Link relacionado: Recebendo dados do usuário: attr_accessible e attr_protected