Recebendo dados do usuário: attr_accessible e attr_protected
26/12/07
O Ruby on Rails permite que você defina os valores dos atributos de um objeto ActiveRecord através dos métodos new e attributes. A esse tipo de atribuição é dado o nome mass assignment. Tal tipo de atribuição facilita muito na hora de instanciar um novo objeto, pois você não precisa fazer a atribuição individualmente. Veja um exemplo.
# the old way
user = User.new
user.login = "rox"
user.email = "rox@example.com"
user.admin = true
# the ruby way
user = User.new(:login => "rox", :email => "rox@example.com", :admin => true)
Os mais atentos acabaram de identificar uma enorme falha de segurança, pois posso definir o tipo de um usuário — admin, neste caso — por enviar o atributo admin no método params.
# the "do whatever you want" way
user = User.new(params[:user])
Se você teve um mínimo de preocupação com a segurança de seu aplicativo, deve ter chegado aos métodos attr_protected ou, melhor ainda, attr_accessible. Ambos estipulam quais atributos podem ser definidos através do mass assignment. A grande diferença entre eles é que o método attr_protected informa quais atributos são protegidos (não podem ser definidos), e o segundo trabalha no caminho contrário: você deve informar quais atributos podem ser definidos por mass assignment. Veja exemplos de como utilizar ambos os métodos.
class User < ActiveRecord::Base
# inform the attributes you want
# to protect
attr_protected :admin
end
class User < ActiveRecord::Base
# inform the attributes you want
# to allow mass assignment
attr_accessible :name, :login, :email
end
Apesar de ser mais trabalhoso, o método attr_accessible, por ser restritivo, é mais seguro. Eu fico com ele, e você?
PHP e SQL Injection
07/11/06
Estava explicando para um amigo meu porque não se deve interpolar uma variável direto no SQL. O grande problema é que ao fazer desta maneira, seu sistema fica suscetível a SQL Injection.
Imagine que você faça algo como abaixo.
<?php
$query = "SELECT name, email, password FROM table WHERE field='".$_GET['field']."' LIMIT 1";
?>
Se alguém passar no parâmetro algo como ' OR 1=1 #, seu banco ficaria totalmente exposto, pois a string final seria modificada com uma condição que será sempre satisfeita.
SELECT name, email, password FROM table WHERE field = '' OR 1=1 #' LIMIT 1
Parabéns! Alguém acabou de pegar todos os registros de sua tabela. Para evitar esse tipo de ataque, é aconselhável usar a função mysql_real_escape_string. Ela faz o devido escape de caracteres potencialmente inseguros. A desvantagem é que você tem que fazer isso para cada um dos parâmetros que você vai passar, o que pode se tornar um pouco massante.
Então, passei para ele uma função que eu usava há algum tempo atrás.
<?php
/**
* @param string $query
* @param mixed $arg1, $arg2...$argN
*/
$QUERY = "";
function query($query)
{
global $QUERY;
$args = func_get_args();
$query = array_shift($args);
foreach ($args as $key => $arg) {
if (is_string($arg)) {
$args[$key] = mysql_real_escape_string($arg);
}
}
array_unshift($args, $query);
$query = call_user_func_array('sprintf', $args);
$QUERY = $query;
return mysql_query($query);
}
?>
Use a função acima da seguinte maneira:
<?php
$query = "SELECT name, email, password FROM table WHERE field='%s' LIMIT 1";
$resource = query($query, $_GET['field']);
while ($row = mysql_fetch_object($resource)) {
printf('<strong>%s:</strong> %s<br/>', $row->name, $row->email);
}
?>
Você pode passar quantos parâmetros precisar, seguindo as regras da função sprintf.
<?php
$query = "INSERT INTO table (amount, age, name) VALUES (%.2f, %d, '%s')";
$resource = query($query, 150, 27, 'Nando Vieira');
?>
Uma outra vantagem de usar a função acima é que se você esquecer de passar algum parâmetro que estava esperando receber, um erro será exibido. E de quebra você ganha uma variável global $QUERY com a última instrução executada.
