Pergunta

Existe um lugar função catchall que funciona bem para saneantes entrada do usuário para injeção de SQL e ataques XSS, enquanto ainda permitindo que certos tipos de HTML tags?

Foi útil?

Solução

É um equívoco comum que a entrada do usuário pode ser filtrada. PHP ainda tem um (agora obsoleto) "recurso", chamado magic-quotes, que se baseia essa idéia. É um absurdo. Esqueça de filtragem (ou limpeza, ou o que as pessoas chamam).

O que você deve fazer, para problemas evitar, é bastante simples: sempre que você inserir uma string no código estrangeira, você deve escapar-lo, de acordo com as regras do que a língua. Por exemplo, se você inserir uma string de alguma SQL visando MySql, você deve escapar da cadeia com a função de MySql para este fim (mysqli_real_escape_string). (Ou, no caso de bancos de dados, usando instruções preparadas são uma abordagem melhor, quando possível)

Outro exemplo é HTML: Se você incorporar cordas dentro HTML marcação, você deve escapar com htmlspecialchars . Isto significa que cada echo ou print declaração deve usar htmlspecialchars.

Um terceiro exemplo poderia ser comandos shell: Se você estiver indo para cadeias incorporar (Tal como argumentos) para comandos externos, e chamá-los com exec , então você deve usar escapeshellcmd e escapeshellarg .

E assim por diante e assim por diante ...

O única caso em que você precisa para filtrar dados ativamente, é se você aceitar a entrada pré-formatado. Por exemplo. se você deixar seus usuários postar HTML markup, que pretende exibir no site. No entanto, você deve ser sábio para evitar isso a todo o custo, pois não importa quão bem você filtrá-la, ela será sempre uma potencial falha de segurança.

Outras dicas

Não tente evitar a injeção SQL por saneantes dados de entrada.

Em vez disso, não permitem dados a serem usados ??na criação do seu código SQL . Use instruções preparado (isto é, utilizando os parâmetros de um modelo de consulta), que utiliza variáveis ??ligadas. É a única maneira de ser garantido contra injeção de SQL.

Por favor, consulte o meu site http://bobby-tables.com/ para saber mais sobre prevenção de injeção de SQL.

No. Você não pode genericamente filtrar dados sem qualquer contexto do que é para. Às vezes você gostaria de ter uma consulta SQL como entrada e, por vezes, você iria querer tomar HTML como entrada.

Você precisa entrada do filtro em uma lista branca - garantir que os dados corresponderem alguma especificação do que você espera. Então você precisa escapar-lo antes de usá-lo, dependendo do contexto em que está a usá-lo.

O processo de fuga de dados para SQL - para evitar a injeção de SQL - é muito diferente do processo de fuga de dados para (X) HTML, para prevenir XSS

.

PHP tem as novas funções agradável filter_input agora, que, por exemplo libertar você de encontrar 'a regex e-mail final' agora que há um built-in tipo FILTER_VALIDATE_EMAIL

A minha própria classe de filtro (usa JavaScript para destacar campos defeituosos) pode ser iniciada por qualquer uma solicitação ajax ou pós forma normal. (Ver exemplo abaixo)

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays. 
 *  Note that some of the regexes are for dutch input!
 *  Example:
 * 
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanatize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanatize);
 *                  
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanatize($_POST);
 *      // now do your saving, $_POST has been sanatized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }   
 *  
 * To validate just one element:
 * $validated = new FormValidator()->validate('blah@bla.', 'email');
 * 
 * To sanatize just one element:
 * $sanatized = new FormValidator()->sanatize('<b>blah</b>', 'string');
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;


    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanatations = $sanatations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanatizes an array of items according to the $this->sanatations
     * sanatations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanatations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanatize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanatations) === false && !array_key_exists($key, $this->sanatations)) continue;
            $items[$key] = self::sanatizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanatize a single var according to $type.
     * Allows for static calling to allow simple sanatization
     */
    public static function sanatizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }

    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       



}

É claro, tenha em mente que você precisa fazer a sua consulta SQL escapando muito, dependendo do tipo de db você está usando (mysql_real_escape_string () é inútil para um servidor SQL, por exemplo). Você provavelmente vai querer lidar com isso automaticamente em sua camada de aplicação apropriada como um ORM. Além disso, como mencionado acima: para a saída de html utilizar as outras funções php dedicado como htmlspecialchars;)

Para realmente permitindo a entrada HTML com como classes e / ou tags despojado depender de um dos pacotes de validação XSS dedicados. Não escrever suas próprias expressões regulares para analisar HTML!

Não, não há.

Em primeiro lugar, a injeção SQL é um problema de filtragem de entrada e XSS é uma saída escapar um -. Para que você não até mesmo executar essas duas operações ao mesmo tempo no ciclo de vida do código

regras básicas

  • Para consulta SQL, ligam-se os parâmetros (como com DOP) ou utilizar uma função de controlador escapar-nativa para variáveis ??de consulta (tais como mysql_real_escape_string())
  • Use strip_tags() para filtrar HTML indesejado
  • escapar de todas outra saída com htmlspecialchars() e estar atento a 2ª e 3ª parâmetros aqui.

Para resolver o problema XSS, dê uma olhada HTML Purifier . É bastante configurável e tem um histórico decente.

Como para os ataques de injeção SQL, certifique-se de verificar a entrada do usuário, e depois executá-lo embora mysql_real_escape_string (). A função não vai derrotar todos os ataques de injeção, embora, por isso é importante que você verifique os dados antes de despejá-lo em sua string de consulta.

Uma solução melhor é usar declarações preparadas. A DOP biblioteca e suporte a extensão mysqli estes.

PHP 5.2 introduziu o função filter_var.

Ele suporta uma grande quantidade de higienizar, filtros valide.

http://php.net/manual/en/function.filter- var.php

Um truque que pode ajudar na circunstância específica onde você tem uma página como /mypage?id=53 e você usar o id em uma cláusula WHERE é garantir que id definitivamente é um inteiro, assim:

if (isset($_GET['id'])) {
  $id = $_GET['id'];
  settype($id, 'integer');
  $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
  # now use the result
}

Mas é claro que apenas cortes fora um ataque específico, assim que ler todas as outras respostas. (E sim, eu sei que o código acima não é grande, mas mostra a defesa específica.)

Métodos de saneantes entrada do usuário com PHP:

  • Versões uso moderno do MySQL e PHP.

  • Set charset explicitamente:

    • $mysqli->set_charset("utf8");
      manual do
    • $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);
      manual do
    • $pdo->exec("set names utf8");
      manual do
    • $pdo = new PDO(
      "mysql:host=$host;dbname=$db", $user, $pass, 
      array(
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
      )
      );
      manual do
    • mysql_set_charset('utf8')
      [obsoleto em PHP 5.5.0, removido em PHP 7.0.0].
  • Uso charsets seguras:

    • Selecionar utf8, latin1, ascii .., o uso não faça vulneráveis ??charsets big5, cp932, gb2312, gbk, sjis.
  • Use a função espacializada:

    • MySQLi declarações preparadas:
      $stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); 
      $param = "' OR 1=1 /*";
      $stmt->bind_param('s', $param);
      $stmt->execute();
    • PDO :: quote () - lugares aspas em torno do cadeia de entrada (se necessário) e escapa caracteres especiais dentro da cadeia de entrada, usando um estilo apropriado citando para o condutor subjacente:

      $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
      $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection)
      $var = $pdo->quote("' OR 1=1 /*");not only escapes the literal, but also quotes it (in single-quote ' characters) $stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

    • DOP declarações preparadas : vs MySQLi preparado declarações suportes mais banco de dados drivers e parâmetros nomeados:

      $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
      $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection) $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(["' OR 1=1 /*"]);

    • mysql_real_escape_string [obsoleto em PHP 5.5.0, removido em PHP 7.0.0].
    • mysqli_real_escape_string Escapa caracteres especiais em uma string para uso em uma instrução SQL, levando em conta o conjunto de caracteres atual da conexão. Mas recomendado o uso de instruções preparadas, porque eles não são simplesmente escapou cordas, uma declaração surge com um plano de execução de consulta completa, incluindo as tabelas e índices que iria usar, é uma forma otimizada.
    • Usar aspas simples (' ') em torno de suas variáveis ??dentro de sua consulta.
  • Verifique a variável contém o que você está esperando para:

    • Se você está esperando um inteiro, use:
      ctype_digit — Check for numeric character(s);
      $value = (int) $value;
      $value = intval($value);
      $var = filter_var('0755', FILTER_VALIDATE_INT, $options);
    • Para uso Cordas:
      is_string() — Find whether the type of a variable is string
      Use Filtro Função filter_var () - Filtra a variável com um especificado filtrar:
      $email = filter_var($email, FILTER_SANITIZE_EMAIL);
      $newstr = filter_var($str, FILTER_SANITIZE_STRING);
      filtros mais predefinidos
    • filter_input () - Obtém uma variável externa específica pelo nome e opcionalmente a filtra:
      $search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match () - Executar uma expressão regular;
    • escrever sua própria função de validação.

O que você está descrevendo aqui é duas questões distintas:

  1. Saneantes / filtragem de dados de entrada do usuário.
  2. Escapando saída.

1) A entrada do usuário deve sempre ser assumido como sendo ruim.

Usando declarações preparadas, e / ou filtragem com mysql_real_escape_string é definitivamente um must. PHP também filter_input construído em que é um bom lugar para começar.

2) Este é um tema grande, e isso depende do contexto dos dados estão a ser enviados. Para HTML existem soluções como HTMLPurifier lá fora. como regra geral, sempre escapar qualquer coisa que você saída.

Ambas as questões são demasiado grandes para entrar em um único post, mas há muitos lugares que entrar em mais detalhes:

saída Métodos PHP

Safer PHP saída

Se você estiver usando PostgreSQL, a entrada de PHP pode ser escapou com pg_escape_string ()

 $username = pg_escape_string($_POST['username']);

A partir da documentação ( http://php.net/manual /es/function.pg-escape-string.php ):

pg_escape_string () escapa uma corda para consultar o banco de dados. Ele retorna um string com escapes no formato PostgreSQL sem aspas.

A maneira mais fácil para erros evitar na sanitização de entrada e fuga de dados está usando framework PHP como Symfony , Nette etc. ou parte desse quadro (templates motor, a camada de banco de dados, ORM).

motor de templates como Twig ou Latte tem saída escapando por padrão - você não tem que resolver manualmente se ter escapado corretamente a saída, dependendo do contexto (HTML ou Javascript parte da página web).

Framework é saneantes automaticamente a entrada e você should't usar $ _POST, $ _GET ou $ _SESSION variáveis ??diretamente, mas através de mecanismo como o encaminhamento, a manipulação da sessão etc.

E para banco de dados (modelo) camada existem frameworks ORM como doutrina ou invólucros em torno de DOP como Nette banco de dados.

Você pode ler mais sobre ele aqui - O que é uma estrutura de software?

Não há nenhuma função catchall, porque há várias preocupações a serem abordados.

  1. SQL Injection - Hoje, em geral, cada projeto PHP deve estar usando instruções preparadas via PHP Data Objects (PDO) como uma boa prática, prevenir um erro de uma citação de rua, bem como uma solução full-featured contra injeção . É também a maneira mais flexível e seguro para acessar seu banco de dados.

    Confira (A única adequada) DOP tutorial para praticamente tudo o que precisa saber sobre PDO. (Agradecimentos sinceros a contribuinte SO topo, @YourCommonSense, para este grande recurso sobre o assunto.)

  2. XSS - dados Sanitize sobre a forma como ...

    • HTML Purifier tem sido em torno de um longo período de tempo e ainda é atualizado ativamente. Você pode usá-lo para limpar a entrada maliciosos, permitindo ainda uma lista branca generosa e configurável de tags. Funciona muito bem com muitos editores WYSIWYG, mas pode ser pesado para alguns casos de uso.

    • Em outros casos, em que não querem aceitar HTML / Javascript em tudo, eu encontrei esta função simples útil (e passou múltiplas auditorias contra XSS):

      /* Prevent XSS input */ function sanitizeXSS () { $_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING); $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST; }

  3. XSS - dados Sanitize na saída ... a menos que você garantir os dados foram devidamente higienizados antes de adicioná-lo ao seu banco de dados, você vai precisar para higienizar-lo antes de exibi-lo para o usuário, que pode alavancar essas funções PHP útil:

    • Quando você chama echo ou print para valores fornecidos pelo usuário de exibição, utilização htmlspecialchars a menos que os dados foram devidamente higienizados seguro e é permitido HTML display.
    • json_encode é uma forma segura de fornecer valores fornecidos pelo usuário de PHP para Javascript
  4. Você chamar comandos shell externos usando exec() ou system() funções, ou para o backtick operador? Se assim for, além de SQL Injection & XSS você pode ter um adicional preocupação para o endereço, usuários que executam comandos maliciosos no seu servidor . Você precisa usar escapeshellcmd se você gostaria de escapar todo o comando OR escapeshellarg para escapar argumentos individuais.

Nunca os dados do usuário confiança.

function clean_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

A função trim() remove espaços em branco e outros caracteres predefinidos de ambos os lados de uma string.

As remove função stripslashes() barras invertidas

A função htmlspecialchars() converte alguns caracteres predefinidos para entidades HTML.

Os caracteres pré-definidos são:

& (ampersand) becomes &amp;
" (double quote) becomes &quot;
' (single quote) becomes &#039;
< (less than) becomes &lt;
> (greater than) becomes &gt;

Só queria acrescentar que no assunto da produção de escapar, se você usar php DOMDocument para fazer a sua saída html vai escapar automaticamente no contexto certo. Um atributo (value = "") eo texto interno de um não são iguais. Para ser seguro contra XSS ler este: OWASP XSS Prevenção Cheat Sheet

Você entrada nunca sanitize.

Você saída sempre sanitize.

As transformadas você aplicar aos dados para torná-la segura para inclusão em uma instrução SQL são completamente diferentes daqueles que você aplicar para inclusão em HTML são completamente diferentes daqueles que você aplicar para inclusão no Javascript são completamente diferentes daqueles que você aplicar para inclusão em LDIF são completamente diferentes daqueles que você aplica a inclusão no CSS são completamente diferentes daqueles que você aplica a inclusão em um e-mail ....

Por todos os meios validar a entrada - decidir se você deve aceitá-lo para posterior processamento ou dizer ao usuário que é inaceitável. Mas não se aplicará nenhuma mudança para a representação dos dados até que ele está prestes a sair PHP terra.

Há muito tempo alguém tentou inventar um one-size fits all mecanismo para escapar de dados e acabamos com " magic_quotes ", que não escapou adequadamente os dados para todas as metas de produção e resultou na instalação diferente exigindo código diferente ao trabalho.

Não é a extensão do filtro ( howto-link , manual do ), que funciona muito bem com todas as variáveis ??GPC. Não é uma mágica-do-it-all coisa, porém, você ainda vai ter que usá-lo.

Eu posso ver php filtro sanitize caracteres especiais especiais vir a calhar.

como:

    $a=fliter_var($_POST['a'],FILTER_SANITIZE_SPECIAL_CHARS);

No entanto, ao estoque, eu acho que poderia ser melhor, porque olhando para o código C, só filtra o "" \ <> & e \ 0 para que eu possa ver este ser uma boa maneira de higienização. No entanto, alterar o código-fonte para incluir esses outros personagens como / {} []; `iria reforçar esta função no codificar (enc [ '']) linha:.

    void php_filter_special_chars(PHP_INPUT_FILTER_PARAM_DECL)
{
unsigned char enc[256] = {0};

php_filter_strip(value, flags);

/* encodes ' " < > & \0 to numerical entities */
enc['\''] = enc['"'] = enc['<'] = enc['>'] = enc['&'] = enc[0] = 1;

/* if strip low is not set, then we encode them as &#xx; */
memset(enc, 1, 32);

if (flags & FILTER_FLAG_ENCODE_HIGH) {
    memset(enc + 127, 1, sizeof(enc) - 127);
}

php_filter_encode_html(value, enc);
}

O melhor método básico para saneantes entrada do usuário com PHP:


    function sanitizeString($var)
    {
        $var = stripslashes($var);
        $var = strip_tags($var);
        $var = htmlentities($var);
        return $var;
    }

    function sanitizeMySQL($connection, $var)
    {
        $var = $connection->real_escape_string($var);
        $var = sanitizeString($var);
        return $var;
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top