SQL INJECTION attack PREVENTION em PHP | code EXAMPLES

após vários anos de testes de penetração e trabalhando com equipes de desenvolvimento para proteger seus sistemas, notei que não havia uma postagem completa no blog que fornecesse uma explicação detalhada de como evitar ataques de injeção SQL em todas as linguagens e estruturas de desenvolvimento de aplicativos da web mais populares. Portanto, decidi escrever uma série de várias postagens para torná-lo o guia mais completo para evitar a injeção de SQL.

este guia é uma série aberta de posts e este é o primeiro post. Se você quiser que eu adicione qualquer tecnologia, por favor, apenas comente abaixo e ficarei muito feliz em explicar a prevenção de injeção de SQL para essa tecnologia. Este guia irá analisar as seguintes tecnologias:

  • PHP
    • Laravel
    • o Symfony
    • CodeIgniter
    • o CakePHP
    • FuelPHP
    • Zend
  • JEE
    • JDBC
    • JPA
  • ASP.NET
    • ADO.NET
    • ADO.NET Dopper
    • NHibernate

Este primeiro post será o foco na linguagem PHP e todos os seus quadros. Mais artigos seguirão para lidar com as outras tecnologias listadas.

para aqueles que não sabem o que é SQL injection attack, deixe-me fazer uma pequena introdução para explicar isso a eles. Os ataques de injeção SQL acontecem quando um usuário ruim tenta injetar uma solicitação SQL maliciosa em uma solicitação legítima. O impacto do SQL injection attack difere de uma situação para outra, dependendo de vários elementos relacionados ao ambiente do aplicativo, e pode ir de tão “simples” quanto o vazamento de informações para um controle completo do servidor. Não vou me aprofundar na maneira como um invasor pode explorar essa vulnerabilidade ou como podemos descobri-la nos testes do blackbox, pois esse não é o objetivo deste post. O que veremos é como descobri-lo no código-fonte e como corrigi-lo.

como evitar o ataque de injeção SQL em um código-fonte PHP puro ?

vamos começar nossa jornada com uma das mais antigas e populares tecnologias de desenvolvimento PHP sem frameworks. Agora, a primeira coisa a fazer é saber como uma linha de código-fonte vulnerável se parece para ser capaz de identificar a vulnerabilidade.

aqui está um exemplo de uma solicitação de autenticação:

$request = "SELECT * FROM users WHERE username=". $_POST ." AND password = ". $_POST;
$result = $mysqli->query($request);

agora, se você der uma olhada na solicitação, notará que os parâmetros do Usuário $_POST e $_POST são injetados diretamente na solicitação SQL sem qualquer filtragem. Isso torna essa linha de código vulnerável à injeção de SQL.

para corrigir essa vulnerabilidade, você precisará filtrar os dados antes de usá-los na solicitação sql. Para fazer isso, a melhor solução é usar as instruções preparadas. Aqui está um exemplo de como você pode usar as instruções preparadas para corrigir essa vulnerabilidade:

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param($username, $password);
$stmt->execute();

Boas práticas

Você tem a possibilidade de não utilizar o bind_param() função aqui é um exemplo de como fazer isso :

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=:username AND password=:password");
$stmt-> execute(array('username'=>$_POST,'password'=>$_POST));

depois de anos analisando códigos-fonte … descobri um erro interessante que muitos desenvolvedores fazem ao corrigir essa vulnerabilidade. Dê uma olhada no exemplo a seguir:

$stmt = $conn->prepare("SELECT * FROM users WHERE username=".$_POST." AND password=".$_POST);

se você olhar para este exemplo, verá que estamos usando as instruções preparadas. No entanto, os parâmetros são injetados diretamente na solicitação SQL. Este é um erro muito comum que os desenvolvedores fazem e tornam o código vulnerável à injeção SQL, mesmo se usarmos as instruções preparadas.

a ideia por trás das instruções preparadas é filtrar os parâmetros antes de injetá-los na solicitação SQL … mantenha isso em sua mente.

nota muito importante :

o PHP oferece algumas outras funções para filtrar solicitações SQL e impedir a injeção de SQL, mas algumas delas não são eficientes. A função mysql_real_escape_string (), por exemplo, é uma das funções PHP mais conhecidas para evitar a injeção de SQL. Infelizmente, essa função é realmente eficiente. Ao usar esta função, a biblioteca MySQL adicionará uma barra invertida aos seguintes caracteres: NULL, \ x00, \ n, \ r, \, ‘, “e \ x1a. No entanto, para uma situação como esta :

$id = mysql_real_escape_string("1 OR 1=1");
$request = "SELECT * FROM users WHERE id = $id";

Na cor vermelha, é o que uma má usuário irá enviar

A mysql_real_escape_string() não irá protegê-lo contra tal ataque.

é por isso que eu sempre recomendo usar as instruções preparadas em vez desta função.

como evitar o ataque de injeção SQL no Laravel framework ?

agora que vimos o ataque de injeção SQL contra um código PHP puro, agora é hora de ver como podemos corrigir a vulnerabilidade em um aplicativo web baseado em framework. Antes de começarmos a falar sobre isso, deixe-me primeiro dar-lhe uma pequena descrição do framework Laravel.Laravel é um framework web de código aberto escrito em PHP respeitando o princípio model-view-controller e inteiramente desenvolvido em programação orientada a objetos.

exemplo 1

aqui está um exemplo de uma solicitação SQL vulnerável em um laravel :

$user = DB::select('select * from users where username='. $request->post('username').' AND password='. $request->post('password'));

o Laravel framework oferece algumas funções incríveis para ajudar os desenvolvedores a proteger a solicitação SQL contra injeções maliciosas. Infelizmente, a maioria das vulnerabilidades de injeção SQL que descobri nesses aplicativos está relacionada ao uso indevido das funções do Laravel.

vamos analisar o exemplo anterior, de acordo com a documentação do Laravel, a função DB::select pode receber dois parâmetros. O primeiro é a solicitação SQL e o segundo são os parâmetros. Assim, para filtrar os parâmetros de injeção de SQL, você precisará inserir os parâmetros na solicitação usando o segundo parâmetro, assim como esse exemplo :

$user = DB::select(‘select * from users where username=? E senha=?’, );

Exemplo 2

Laravel tem várias maneiras de se comunicar com o banco de dados neste exemplo você verá que laravel força o desenvolvedor a utilizar algumas funções que filtrar automaticamente os dados do usuário, como o exemplo a seguir:

Users::where('username', $request->get('username'))->orderBy($request->get('orderby'))->get();

esta função deve ser protegida contra injeção SQL e eles são, o único problema está no nível da função orderBy (). De acordo com a documentação do Laravel, essa função não filtra os dados do usuário, portanto, um ataque de injeção SQL ainda é possível por meio dessa função.

a melhor coisa A fazer é não dar ao usuário a possibilidade de controlar o nome da tabela, mas Se isso é algo inevitável, então você vai precisar usar uma lista branca para validar os dados do usuário antes de inseri-lo nessa função.

como evitar o ataque de injeção SQL no Symfony ?Vamos ver outro framework PHP bem conhecido que oferece um conjunto de componentes PHP reutilizáveis para acelerar o desenvolvimento de aplicativos PHP. Symfony também é usado por alguns dos CMS da web mais conhecidos, como Drupal e Magento.

Symfony é uma das estruturas mais seguras com as quais você pode trabalhar, oferece um grande número de funções para escrever um código seguro. No entanto, se essas funções não forem bem usadas, você obterá um código vulnerável. Então, aqui está um exemplo de um código vulnerável:

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > '. $request->query->get('price')
);

agora vamos analisar esse código vulnerável. De acordo com a documentação do Symfony, createQuery() é uma função que usa por padrão as instruções preparadas. Portanto, o objeto resultante dessa função fornece acesso à função setParameter (). Este filtra todos os dados do Usuário para evitar uma injeção SQL. Aqui está um exemplo de como usá-lo :

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', $price);

como evitar o ataque de injeção SQL no Codeigniter ?

CodeIgniter um dos frameworks PHP mais poderosos, leves e populares com uma pegada muito pequena. Foi construído para desenvolvedores que precisam de um kit de ferramentas simples e elegante para criar aplicativos da web completos. Como Symfony e Laravel, Codeigniter também vem com alguns sistemas de segurança para ajudar os desenvolvedores a criar aplicativos mais seguros.

no entanto, também com Codeigniter alguns desenvolvedores cometem o mesmo erro e injetam os dados do usuário sem qualquer filtragem. Aqui está um exemplo de tais erros, que levam a um ataque de injeção de SQL:

$query = 'SELECT * FROM users WHERE username = '. $this-> input->post('username'). ' AND password= '. $this-> input->post('password');
$this->db->query($query);

Agora para corrigir esse problema, você precisará dar uma olhada na documentação do Codeigniter. De acordo com ele, a função query() leva dois parâmetros. O primeiro é a consulta sql e o segundo são os parâmetros que você deseja vincular.

exemplo 1

por padrão, esta função filtra todos os parâmetros encadernados para evitar injeções de SQL. Aqui está um exemplo da maneira correta de usar esta função:

$query = 'SELECT * FROM users WHERE username = ? AND password = ? ';
$this->db->query($query, array($this->input->post('username'), $this-> input-> post('password')));

Exemplo 2:

o Codeigniter fornece outra maneira de executar a solicitação SQL impedindo as injeções de SQL. Aqui está um exemplo de uso da classe Active Record para evitar injeções de SQL:

$this->db->get_where('users',array('username'=>$this->input->post('username') ,'password' => $this->input->post('password')));

a função get_where () não existe mais na versão 4 do Codeigniter.

Nota 1

Codeigniter também oferece uma função que se parece com, mysql_real_escape_string () função chamada escape (). Mas, como eu disse pela primeira vez, o mysql_real_escape_string() pode ser ignorado em alguns casos e você precisa evitar usá-lo. O fato de que a função escape () faz exatamente o mesmo, então seria possível contorná-la também. É por isso que não encorajo os desenvolvedores a usar a função escape ().

Nota 2

como evitar o ataque de injeção SQL no CakePHP ?CakePHP é uma estrutura de desenvolvimento rápido para PHP que usa padrões de design comumente conhecidos como mapeamento de dados associativos, controlador frontal e MVC. O CakePHP oferece um monte de componentes integrados para facilitar o desenvolvimento de aplicativos da web e os tornará mais seguros.

no entanto, os aplicativos CakePHP também são vulneráveis a injeções de SQL se não forem bem usados. Aqui está um exemplo de um código vulnerável ao ataque de injeção SQL nesta estrutura:

$results = $connection-> execute('SELECT * FROM users WHERE username = '. $this->request->getParam('username').' AND password='. $this->request->getParam('password'))->fetchAll('assoc');

a função executar () por padrão usa as instruções preparadas. No entanto, o exemplo anterior ainda vulnerável ao ataque de injeção SQL, pois os dados do usuário são inseridos diretamente em uma solicitação sql legítima. Aqui está um exemplo da maneira correta de usar esta função:

$results = $connection->execute('SELECT * FROM users WHERE username = :username AND password=:password', )->fetchAll('assoc');

a estrutura CakePHP também oferece um sistema chamado Query Builder, que força a filtragem de dados do usuário a evitar ataques de injeção SQL. De acordo com a documentação do CakePHP, abaixo das capas, O Construtor de consultas usa as instruções preparadas.

aqui está um exemplo de como você pode usar esse sistema da maneira certa:

use Cake\ORM\Locator\LocatorAwareTrait;
$users = $this->getTableLocator()->get('users');
// Start a new query.
$query = $users->find();
$query->where();

como evitar o ataque de injeção SQL no FuelPHP ?

o FuelPHP é um dos framework PHP mais recentes que nasceu com base nas melhores ideias de cada framework do mercado. Foi desenvolvido com PHP 5 e é totalmente orientado a objetos. No FuelPHP, a segurança era a frente e o centro das preocupações, o que levou seus colaboradores a implementar muitos mecanismos de segurança para filtrar os dados do Usuário. A injeção de SQL ataca uma das razões para implementar tais mecanismos no FuelPHP.

no entanto, mesmo com uma estrutura tão poderosa, um simples uso indevido desses mecanismos colocou todo o código em perigo para um ataque de injeção SQL. Aqui está um exemplo de tal erro de código:

$query = "SELECT * FROM article WHERE id = ". Input::get('id');
$result = DB::query($query)->execute();

para corrigir isso, também existem duas técnicas como as outras estruturas. O primeiro é realmente usar corretamente a função query (), vinculando os parâmetros como o exemplo a seguir :

$query = "SELECT * FROM article WHERE id = :id"; // our query
$result = DB::query($query)->bind('id', Input::get('id'))->execute();

a segunda solução é usar o mecanismo ORM implementado na estrutura FuelPHP para se comunicar com o banco de dados. Aqui está um exemplo de como usá-lo:

$user = DB::select()->from('article')->where('id', Input::get('id'))->execute();

como evitar o ataque de injeção SQL no zend framework ?

o Zend framework (alterado para Laminas Project) é um dos framework mais conhecidos no mundo do PHP. Foi desenvolvido com o ajuste de desempenho em mente, o que explica por que cada nova versão é muito mais rápida que a antiga. Zend também foi desenvolvido com as melhores práticas de segurança, o que levou seus desenvolvedores a implementar mecanismos de segurança adicionais para lidar com ameaças cibernéticas conhecidas.

alguns desses mecanismos foram implementados para lidar com os ataques de injeção SQL. Mas, como sempre, um mau uso desses mecanismos leva a um código vulnerável. Nesta parte do artigo, mostrarei um exemplo de tal erro:

$adapter->query('SELECT * FROM `article` WHERE `id` = '. $this->getRequest()->getPost('id'));

veja como corrigir essa vulnerabilidade:

$adapter->query('SELECT * FROM `article` WHERE `id` = ?', );

Leave a Reply