+5

Principais Falhas de Segurança em PHP

criado por Diego R. Vieira em 30/03/2006 5:04pm
SQL Injection
-------------
SQL injection ganhou uma atenção grande nos últimos tempos com mais e mais sites que estão com seus bancos de dados expostos a falhas. O nível de perigo depende de banco de dados que você está usando. MSSQL e Oracle são notavelmente ruins, com MSSQL tendo o comando xp_cmdshell permite executar qualquer comando virtualmente no server. Eu irei dar foco ao MySql, pois eu realmente não uso outros DBs e MySql é uma combinação muito comum com Php. (postgresql inicialmente é outro muito mais competidor)

Vamos supor que você tem um simples sistema de login, que valida usuários do banco dados:
(estamos supondo que magic_quotes esteja desligado)

<?php
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM `users` WHERE username='$username' AND password='$password'";
$result = mysql_query($query);
if(mysql_num_rows($result) > 0) {
    $admin = 1;
} else {
    $admin = 0;
} 
if($admin == 1) {
    // blah blah, restricted content here.
}
?>

Certo, então o que isso faz é pegar o username e o password recebidos do formulário e checar na database se eles se correspondem. Vamos assumir como um usuário valido, "admin", por exemplo. Se nós colocarmos no campo de login da pagina:
admin'#

O que acontecerá?
Vamos reescrever sql com as variáveis expandidas:

<?php
$query = "SELECT * FROM `users` WHERE username='admin'#' AND password='xxx'"; 
?>

OK, então... O problema aqui é que as aspas simples não estão sendo escapadas adequadamente. No MySql, o símbolo (#) é considerado como um comentário, como as barras duplas (//) no php ou C++, tudo após o (#) passa a ser ignorado pois passa a ser um comentário, ignorando a verificação da senha. Obviamente isto só irá funcionar se você tiver um usuário válido.
No entanto, você não precisa necessariamente saber um usuário válido usando: ' OR 1=1#

Irá se tornar...

<?php
$query = "SELECT * FROM `users` WHERE username='' OR 1=1#' AND password='xxx'"; 
?>

Isso irá selecionar todos os usuários do banco de dados. Se a função de login apenas contar o numero de linhas retornadas apos a query como a do exemplo. Então provavelmente irá assumir os dados do primeiro usuário retornado. Que pode ser possivelmente um admin.
Há várias coisas perigosas que você fazer com sql injection, mas elas estão além do objetivo desse artigo. Tudo que você precisa saber é a existência dela e como se proteger contra ela. O problema principal são as aspas simples serem interpretadas como parte da query. Que OBVIAMENTE não é o que nós queremos. O precisa ser feito é sempre ter certeza que elas serão escapadas, de forma que ' se tornaria \' (\ é usado quando se escapa uma string). O melhor modo para fazer isto é usar a função nativa do PHP mysql_escape_string, como o exemplo abaixo:

<?php
$username = mysql_escape_string($_POST['username']);
$password = mysql_escape_string($_POST['password']);
$query = "SELECT * FROM `users` WHERE username='$username' AND password='$password'";
//etc etc
?>


Agora se nós tentarmos entrar com o campo username contendo admin'# aconteceria o seguinte:
<?php
$query = "SELECT * FROM `users` WHERE username='admin\'#' AND password='xxx'"; 
?>


As aspas simples são escapadas com sucesso e a query não está mais vulnerável.

Se você sabe que uma determinada variável irá ter o valor de inteiro, então o melhor a ser feito é:
<?php
$offset = (int)$_GET['offset'];
$query = "SELECT * FROM `cakes` LIMIT 20 OFFSET '$offset'";
?>


Então só um valor numérico pode ser passado deste modo, se um texto for enviado o valor da variável $offset será 0.

Praticamente tudo precisa ser escapado antes de ser enviado ao banco de dados. Os programadores tendem a esquecer coisas que não estão vindo das variáveis GET/POST, como o HTTP_REFERER ou HTTP_USER_AGENT, estes podem ser facilmente modificados para conter uma sql injection que deve ser escapado antes de ser colocado em uma query.

Há uma opção do PHP que pode ser configurada no php.ini, que se chama magic_quotes_gpc que escapa todos os dados vindos de variáveis GET/POST/COOKIE. Isso pode causar problemas quando você escapa strings, pois poderia dobrar barras assim (\\\'). Quer um conselho? Nunca programe como se no servidor isso estivesse habilitado, pois talvez em outro ele não esteja.

'Criei' uma função simples que sempre me ajuda... Então sempre use isso em variáveis vindas de GET/POST/COOKIE ;)

<?php
function escapestrings($b) {
    //se magic_quotes não estiver ativado, escapa a string
    if (!get_magic_quotes_gpc()) {
        return mysql_escape_string($b); // função nativa do php para escapar variáveis.
    } else { 
        // caso contrario
        return $b; // retorna a variável sem necessidade de escapar duas vezes
    }
}
?>

Exemplo:
<?php
$username = escapestrings($_POST['username']);
$password = escapestrings($_POST['password']);
$query = "SELECT * FROM `users` WHERE username='$username' AND password='$password'";
//etc etc
?> 

Nota: Para você tirar as barras que foram incluídas para escapar,quando for mostrar os dados para o usuário
use stripslashes(). Para evitar barras desnecessárias ;)

<?php
$article = mysql_fetch_array($fnord);
echo $article['title']; // Badger\'s Parade
echo stripslashes($article['title']); // Badger's Parade
?>

Comentários:

Mostrando 1 - 10 de 26 comentários
Valeu pelo toque. Vou prestar mais atenção nesses detalhes.
26/10/2006 4:06pm (~17 anos atrás)

Falhas ou Não, é necessário estár atendo as possíveis invasões... de qualquer forma o artigo é muito bom, parabéns!!!
15/09/2006 5:23am (~17 anos atrás)

Ok, se você não conhece outros DB, não deveria ter tanta propriedade em desmerecê-los em favor do MySql que é muito mais limitado - inclusive quanto à segurança de banco e não aplicação - que os grandes de mercado. Há muitas funcionalidades disponíveis em outros DB que não se encontram no Mysql e que os DBAs necessitam
06/07/2006 5:41pm (~18 anos atrás)

Não é falhas de seguranca do PHP, acho que você leu errado não?
E sim falhas de segurança "em" PHP
abraços
29/06/2006 2:18pm (~18 anos atrás)

Adler Medrado disse:
Eu não concordo quando diz falhas de seguranca do PHP.
Acho que existem falhas de seguranca do programador. O PHP por facilitar muito as coisas, permite que os programadores facam muitas besteiras.
[]s

adler medrado
www.neshertech.net
adler.neshertech.net
25/06/2006 7:34am (~18 anos atrás)

Cara!
confesso que muita coisa que vc falou, eu já deu mole!!
muito obrigado pelas dicas!!
abraços!!
12/06/2006 4:13am (~18 anos atrás)

Daniel Santos disse:
Cara estou aconpanhando esse artigo e ele esta muito bom.
08/06/2006 5:10am (~18 anos atrás)

Fabiano Shark disse:
Como o artigo não é seu, somente a tradução, congratulo-o pela iniciativa.
21/05/2006 8:30pm (~18 anos atrás)

Primeiramente OTIMO artigo. Eu sou bem novato em php e este artigo vei muito bem a calhar.

No caso de tratamento de query diretamente tambem poderia ser utilizado:

$login = isset($_POST["login"]) ? addslashes(trim($_POST["login"])) : FALSE;

Desta forma todos os dados escapados como aspas simpes(') e aspas duplas(") seria vistos com caracteres normais.

Prontinho minha contribuicao.

Abraco
07/05/2006 6:11pm (~18 anos atrás)

Realmente alguns são bons programadores, porém acabam se esquecendo de um fator importante: segurança. Apenas um deslize poderá ser fatal um dia... Parabéns pelo artigo :-)
29/04/2006 5:47pm (~18 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)