downloads | documentation | faq | getting help | mailing lists | licenses | wiki | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Relatando Erros> <Modelo de Armazenamento Criptografado
Last updated: Fri, 06 Nov 2009

view this page in

Injeção de SQL

Muitos desenvolvedores web não sabem de como consultas SQL podem ser manipuladas e presumem que uma consulta de SQL é um comando confiável. Significa que consultas SQL são capazes de passar indetectado por controles de acesso, portanto desviando da autenticação padrão e de checagens de autorização, e algumas vezes consultas SQL podem permitir acesso à comando em nível do sistema operacional do servidor.

Injeção direta de comandos SQL é uma técnica onde um atacante cria ou altera comandos SQL existentes para expor dados escondidos, ou sobrescrever dados valiosos, ou ainda executar comandos de sistema perigosos no servidor. Isso é possível se a aplicação pegar a entrada do usuário e combinar com parâmetros estáticos para montar uma consulta SQL. Os exemplos a seguir são baseados em histórias verdadeiras, infelizmente.

Devido à falta de validação de entrada e conectando ao banco de dados usando o super-usuário ou um usuário que pode criar usuário, o atacante pode criar um super-usuário no seu banco de dados.

Exemplo #1 Dividinto o result set em páginas ... e criando super-usuários (PostgreSQL)

<?php

$offset 
$argv[0]; // Cuidado, sem validação de entrada!
$query  "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result pg_query($conn$query);

?>

Usuários normais clicam nos links 'próxima' e 'anterior' onde $offset é codificado na URL. O script espera que o valor de $offset seja um número decimal. No entanto, e se alguém tentar invadir acrescentando a forma codificada por urlencode() da URL seguinte:

0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select 'crack', usesysid, 't','t','crack'
    from pg_shadow where usename='postgres';
--

Se isso acontecesse, então o script daria de presente acesso de super-usuário ao atacante. Perceba que 0; é para fornecer uma deslocamento válido para a consulta original e terminá-la.

Nota: É uma técnica comum forçar o avaliador de SQL ignorar o resto da consulta escrita pelo desenvolvedor com --, que é o sinal de comentário no SQL.

Uma maneira de ganhar senha é desviar suas páginas de resultado de busca. A única coisa que o atacante precisa fazer é ver se alguma variável enviada é usada em um comando SQL que não é tratado corretamente. Esses filtros podem ser configurados de forma a personalizar cláusulas WHERE, ORDER BY, LIMIT e OFFSET em comandos SELECT Se seu banco de dados suporta o construtor UNION, o atacante pode tentar adicionar uma consulta inteira à consulta original para listar senhas de uma tabela arbitrária. Uso de campos de senha criptografados é fortemente incentivado.

Exemplo #2 Listando artigos ... e algumas senhas (qualquer banco de dados)

<?php

$query  
"SELECT id, name, inserted, size FROM products
                  WHERE size = '
$size'
                  ORDER BY 
$order LIMIT $limit$offset;";
$result odbc_exec($conn$query);

?>

A parte estática da consulta pode ser combinada com outro comando SELECT que revela todas as senhas:

'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--

Se essa consulta (brincando com ' e --) fosse atribuída para uma das variáveis usadas em $query, a consulta boba acordaria.

Comandos de UPDATE também são suscetíveis a ataques. Essas consultas também são ameaçadas por cortes e acréscimos de uma nova consulta. Mas o atacante pode brincar com a cláusula SET. Nesse caso ele precisa estar de posse de alguma informação sobre o esquema para manipular a consulta com sucesso. Isso pode ser conseguido examinando os nomes das variáveis do formulário, ou simplesmente por força bruta. Não existem muitas convenções para campos guardando senhas ou nomes de usuários.

Exemplo #3 De reinicializando uma senha ... para ganhando mais privilégios (qualquer banco de dados)

<?php
$query 
"UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>

Mas um usuário malicioso envia o valor ' or uid like'%admin%'; -- para $uid para mudar a senha do administrador, ou simplesmente configura $pwd para "hehehe', admin='yes', trusted=100 " (com um espaço sobrando) para ganhar mais privilégios. Então, a consulta ficará torta:

<?php

// $uid == ' or uid like'%admin%'; --
$query "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";

// $pwd == "hehehe', admin='yes', trusted=100 "
$query "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE
...;"
;

?>

Um exemplo assustador de como comandos do sistema operacional podem ser acessados em alguns bancos de dados.

Exemplo #4 Atacando o sistema operacional do servidor (MSSQL Server)

<?php

$query  
"SELECT * FROM products WHERE id LIKE '%$prod%'";
$result mssql_query($query);

?>

Se o atacante enviar o valor a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- para $prod, então $query terá o valor:

<?php

$query  
"SELECT * FROM products
                    WHERE id LIKE '%a%'
                    exec master..xp_cmdshell 'net user test testpass /ADD'--"
;
$result mssql_query($query);

?>

MSSQL Server executa os comandos SQL em um lote incluindo um comando para adicionar um novo usuário para o banco de dados de contas locais. Se essa aplicação estiver sendo executada como sa e o serviço MSSQLSERVER estivesse sendo executado com privilégios suficientes, o atacante teria agora uma conta com a qual poderia acessar essa máquina.

Nota: Alguns dos exemplos acima estão ligados a bancos específicos. Isso não significa que um ataque similar é impossível contra outros produtos. Seu servidor de banco de dados pode ter uma vulnerabilidade similar de outa maneira.

Técnicas para Evitar Ataques

Você pode dizer que o atacante precisa possuir um pouco de informação sobre o esquema de banco de dados na maioria dos exemplos. Você tem razão, mas você nunca sabe quando e como isso pode ser obtido e, se acontecer, seu banco de dados pode ficar exposto. Se você estiver usando um pacote open source publicamente disponível para lidar com banco de deados, que pode pertencer a um sistema de controle de conteúdo ou forum, os invasores facilmente produzem uma cópia de parte de seu código. Também pode ser um risco de segurança se este for for mal desenhado.

Esses ataques se baseam principalmente em explorar falhas no código escrito sem se preocupar com segurança. Nunca confie em nenhum tipo de entrada, especialmente aquela que vem do lado do cliente, mesmo que venha de um combobox, um campo de entrada escondido (hidden) ou um cookie. O primeiro exemplo mostra como uma consulta inocente pode causar desastres.

  • Nunca conecte ao banco de dados como um super-usuário ou como o dono do banco de dados. Use sempre usuários personalidados com privilégios bem limitados.
  • Verifique se uma entrada qualquer tem o tipo de dados experado. O PHP tem um grande número de funções de validação de entrada, desde as mais simples encontrada em Funções de Variáveis e em Funções de Tipo de Caracteres (ex.: is_numeric(), ctype_digit() respectivamente) além de usar o suporte a Expressões Regulares Compatível com Perl.
  • Se a aplicação espera por entradas numéricas, considere verificar os dados com a função is_numeric(), ou silenciosamente mudar o seu tipo usando settype(), ou usar a representação númerica usando a função sprintf().

    Exemplo #5 Uma maneira mais segura para compor consultas de paginação

    <?php

    settype
    ($offset'integer');
    $query "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";

    // por favor perceba o %d na string de formato, usando %s seria inútil
    $query sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
                     
    $offset);

    ?>

  • Adicione aspas para cada valor não numérico especificado pelo usuário que será passado para o banco de dados com as funções de caracteres de escape (ex.: mysql_real_escape_string(), sqlite_escape_string(), etc.). Se um mecanismo de escape de caracter específico para o seu banco de dados não for disponível, as funções addslashes() e str_replace() podem ser úteis (dependendo do tipo de banco de dados). Veja o o primeiro exemplo. Como o exemplo mostra, adicionar aspas à parte estática da consulta não é suficiente, fazendo com que a consulta seja facilmente atacada.
  • Não imprima qualquer informação específica do banco de dados, especialmente sobre o esquema, custe o que custar. Veja também Relatório de Erros e Funções de Tratamento e Relatório de Erros.
  • Você pode usar stored procedures e cursores previamente definidas para abstrair acesso aos dados para que os usuários não acessem tabelas ou views diretamente, mas essa solução pode ter outros impactos.

Além disso, você ganha em relatar consultas ou dentro do script ou no próprio banco de dados, se esse suportar. Obviamente, o relatório é para previnir qualquer tentativa danosa, mas pode ser útil para ajudar a rastrear qual aplicação foi atacada. O relatório não é útil em si, mas atráves da informação que ele contém. Mais detalhes geralmente é melhor que menos.



Relatando Erros> <Modelo de Armazenamento Criptografado
Last updated: Fri, 06 Nov 2009
 
add a note add a note User Contributed Notes
Injeção de SQL
fyrye
06-Aug-2009 10:59
Another way to prevent SQL injections as opposed to binary, is to use URL Encoding or Hex Encoding.
I haven't seen a complete example of stopping SQL Injections, most refer to use the mysql_real_escape_string function or param statements.

Several examples at http://en.wikipedia.org/wiki/SQL_injection

Which will stop \x00, \n, \r, \, ', " and \x1a based attacks.
Alot depends on your SQL query structure, though vector level attacks are still viable.

Other than that build your own regex replacement to protect specific queries that could alter or compromise your database/results for specific sections of your processing pages.
Also use unique table and field names. Not just putting _ infront of them...
Example, don't store User/s or Customer/s information in a table named the same.
And NEVER use the same form field names for database field names.
bendikt [at] armed [dot] nu
27-Jul-2009 02:31
i just played around with the array_walk function.
It suddenly struck me that almost all super globals are arrays.
So what i discovered was that i can apply the array_walk function to the super globals. Doing so you automatically run a function call through the super globals
With this piece of code i wrote you should be able to secure most of you input data.

<?php

class secure
{
    function
secureSuperGlobalGET(&$value, $key)
    {
       
$_GET[$key] = htmlspecialchars(stripslashes($_GET[$key]));
       
$_GET[$key] = str_ireplace("script", "blocked", $_GET[$key]);
       
$_GET[$key] = mysql_escape_string($_GET[$key]);
        return
$_GET[$key];
    }
   
    function
secureSuperGlobalPOST(&$value, $key)
    {
       
$_POST[$key] = htmlspecialchars(stripslashes($_POST[$key]));
       
$_POST[$key] = str_ireplace("script", "blocked", $_POST[$key]);
       
$_POST[$key] = mysql_escape_string($_POST[$key]);
        return
$_POST[$key];
    }
       
    function
secureGlobals()
    {
       
array_walk($_GET, array($this, 'secureSuperGlobalGET'));
       
array_walk($_POST, array($this, 'secureSuperGlobalPOST'));
    }
}

?>
Note that you can modify this in anyway to suit your needs.
The Script has been tested.
Quietust
03-Jun-2009 12:23
An anonymous comment below suggests using PEAR with prepare() / execute() - though it was posted 3 years ago, it is still true today, though it's even easier now since PDO is included in most distributions. For SET/WHERE clauses and INSERT statements, just prepare the query with question marks in place of the dynamic parameters, bind each value in, then execute it, and it'll do all of the escaping for you, rendering the query immune to injection. Dynamic substitution of ORDER BY or LIMIT clauses has to be done the old fashioned way, though, so you still need to be careful with those.

Even without PDO, if you're using Postgres, you've already got the means to use parameterized queries, and if you're using MySQL, you simply need to ignore the outdated "mysql" extension and use "mysqli" instead.
fOV
12-Nov-2008 12:53
The best prevention is to deactivate master..xp_cmdshell.
Run in isql the command `sp_configure 'xp_cmdshell''
If the value for "config value" is 1 then make in the isql
`sp_configure 'xp_cmdshell',0'.
If you want see the options from your mssql-Server make
`sp_configure 'show advanced options',1'
and then `sp_configure'
jaimthorn at yahoo dot com
13-Oct-2008 09:32
dark dot avenger at email dot cz wrote:

"I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s."

Unless there is a 1-to-1 correspondence between your inputted data and the characters in your 'binary' format, a SELECT query wouldn't work anymore.  Not a binary format, but it makes my point: MIME encoding the text 'Dark Avenger' results in 'RGFyayBBdmVuZ2Vy'.  If I wanted to look up anyone with 'Avenger' in his/her name, then 'Avenger' would be encoded as 'QXZlbmdlcg==' which clearly wouldn't result in a hit on 'RGFyayBBdmVuZ2Vy'.

If there IS a 1-to-1 correspondence, then EITHER your solution only makes it a bit harder to perform a SQL injection (a hacker would have to figure out what mapping was used between the text and the 'binary' format), OR you've come up with simply another way to escaping your data.  Either isn't a terribly good solution to the SQL injection problem.
dark dot avenger at email dot cz
14-Aug-2008 08:09
I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s.
valerylourie at gmail dot com
15-Apr-2008 07:23
Note that PHP 5 introduced filters that you can use for untrusted user input:
http://us.php.net/manual/en/intro.filter.php
ctm at etheon dot net
01-Aug-2006 03:39
This is a very helpful document from the MySQL site (in .pdf format) :

http://dev.mysql.com/tech-resources/articles/
guide-to-php-security-ch3.pdf
17-Mar-2006 05:48
If you use the PEAR package and prepare() / execute() your queries,
you will hardly have to worry about any of this. Of course, it's still
a good idea to make sure you're putting valid data in your database...
anonymous
25-Jan-2005 08:42
Here is a useful class that deals with SQL injection:

http://www.phpinsider.com/php/code/SafeSQL/

Relatando Erros> <Modelo de Armazenamento Criptografado
Last updated: Fri, 06 Nov 2009
 
 
show source | credits | stats | sitemap | contact | advertising | mirror sites