Este artigo é a quarta parte da série Como Criar Plugins para WordPress. O WordPress é uma das ferramentas para gestão de sites que tem vindo a crescer mais rapidamente nos últimos anos. Uma das razões provém da sua arquitetura altamente extensível através de plugins a par com a sua API simples de implementar. Recentemente um dos nossos leitores contatou-nos indicando que estava tendo dificuldades em encontrar tutoriais WordPress sobre criar plugins, e sugeriu-nos então que publicássemos uma série de artigos sobre como criar plugins para WordPress, ideia essa que decidimos aceitar!

SUMÁRIO

  1. Permissões dos Usuários
  2. Secrets e Nonces para autorização
  3. Validação e saneamento dos dados postados
  4. Formatando elementos para uso de SQL
  5. Hábitos aconselhados

Falar em “segurança” no mundo da informática leva-nos sempre à noção de perigo constante. Para aqueles que se confrontam com estes problemas todos os dias, a segurança no código, seja este para a web ou para desktop, é sempre a última tarefa mais importante.

As ameaças são constantes: ataques de XSS, CSRF, injecções no SQL, ganho de permissões, vulnerabilidades e buracos de acesso.

Estas ameaças existem e qualquer autor de plugins do WordPress deve estar consciente disso! Se você distribuir o seu plugin na comunidade, deve ter em consideração ainda mais o facto de manter uma responsabilidade para com os dados dos usuários que usam os seus plugins.

Na escala dos principais problemas que os usuários toleram, o menor é a questão de segurança. Se for descoberto uma falha de segurança num plugin, os usuários acabam por deixar de usar os seus plugins e descredibilizam o autor.

Por estas razões, nunca devemos deixar de lado ou para segundo plano a questão da segurança, pois pode comprometer todo o nosso trabalho e reputação.

Segurança no WordPress

O WordPress já implementa várias soluções que ajudam nesta tarefa. Não é dificil e o tempo consumido é pouco, por isso vale sempre a pena!

PERMISSÕES DOS USUÁRIOS

Este é um dos temas mais importantes no que toca a segurança, senão o mais importante. Um bom sistema de permissões no software permite que muitos dos problemas de segurança seguintes não comprometam o sistema, pois brechas, validações, etc, embora possam existir necessitam por parte do usuário de permissão de acesso.

A API de Acessos do WordPress é versátil o suficiente para nos manter seguros dos perigos adjacentes. Nesta matéria existe uma função que nos faz todo o trabalho: current_user_can()

Esta função aceita como argumento uma capacidade ou papel, e testa se o usuário atual tem ou não essa permissão, retornando valores boleanos (true ou false). Caso o usuário não esteja logado, a função irá retornar sempre false – é aqui que reside a segurança para o exterior.

Papeis e Capacidades

As capacidades não são mais que uma tabela identificativa de determinadas ações que um usuário pode efetuar no WordPress. Para cada capacidade está associada uma ação. Sendo assim, um conjunto de capacidades, isto é, ações, formam um papel de usuário. Por exemplo: enquanto que com o papel de Administrador no WordPress, o usuário tem acesso a todas as ações existentes, ou seja, é-lhe atribuido todas as capacidades, um usuário com o papel de Subscritor, apenas tem a capacidade read, que lhe garante acesso apenas à Dashboard e ao perfil do usuário.

O sistema de papeis e capacidades é fantástico, pois permite-nos adicionar ou remover capacidades através de plugins, como visto nos artigos anteriores de como criar custom post types.

Função de avaliação de permissão de acesso

A melhor forma de checar se um usuário tem permissões de acesso é usando a função current_usar_can( $capacidade_ou_papel ). Esta função aceita como argumento tanto uma capacidade como um conjunto de capacidades, ou seja, um papel:

<?php

// Vamos checar por uma capacidade
if ( !current_user_can( 'manage_options' ) )
  wp_die( 'Permissão de acesso insuficiente' );

// Vamos checar por um papel
if ( !current_user_can( 'editor' ) )
  wp_die( 'Permissões abaixo do Editor' );

// Fazer o resto do código...

?>

Com este snippet dentro de qualquer função do nosso plugin, nós evitamos que usuários sem permissão de acesso às ações que implementamos as acessem.

SECRETS E NONCES PARA OBTER AUTORIZAÇÃO 

Este é um caso de ataque comum nos emails de spam e salas de conversação IRC ou de Instant Messaging.

Imagine que um usuário tem permissão para remover posts e encontra-se logado no sistema. No entanto, um outro usuário mal intensionado força o primeiro usuário a clicar num link encapsulado que mascara o link do site para deletar os posts. Como o usuário não sabe disto, ele acaba por remover os posts sem intensão. Acabámos de assistir a um ataque CSFR, ou seja, o usuário mal intensionado ganhou autorização.

Porque é que isto aconteceu? A resposta é: Autorização mas não Intensão!

A análise da anatomia deste ataque ajuda-nos a explicar este fenómeno. O usuário mal intensionado forçou o usuário logado no WordPress e com autorização para remover posts a praticar essa ação:

  • Como o usuário está logado no sistema e tem permissão, a função current_user_can() retornou true.
  • A permissão do usuário não checa se o usuário tem realmente a intensão de praticar aquela ação.

Como resolver esta situação? Felizmente o WordPress detém de um sistema de proteção contra este tipo de ataques. A solução é chamada de nonces, que são simplesmente strings de letras e números aleatórios que são criados durante o carregamento de uma página e colocados no final do link.

Este é um exemplo de um nonce colocado no link de remoção de um post para o lixo:

http://exemplo.com/wp-admin/post.php?post=54&action=trash&_wpnonce=89f5h3j54bc3

A string nonce encontra-se nesta parte _wpnonce=89f5h3j54bc3, em que a senha 89f5h3j54bc3 é gerada aleatoriamente segundo estas regras:

  • Uma senha só é gerada uma vez para um único usuário
  • A senha gerada só respeita uma ação (remover, editar, guardar, etc) para um objeto (post, página, link, etc)
  • Esta senha é um transient guardado por apenas 24h

Como criar uma senha nonce:

Existem dois métodos de gerar uma senha, sendo que um é mais habitual na geração de links e outro para formulários que usem o método POST.

<?php
// Criar uma senha nonce que será colocada no final
// do URL dado. O resultado da função wp_nonce_url()
// será o próprio URL com a senha no final.
$url = wp_nonce_url( admin_url( 'post.php?post=54&action=trash' ) , 'ewp_post_trash_54' );

?>

O URL retornado seria hipoteticamente o mostrado em cima. O segundo parâmetro da função wp_nonce_url() é um identificador que deve ser único: use o prefixo do seu plugin em conjunto com o objeto referido e a ação a que respeita.

Outro método importante é a função wp_nonce_field():

<?php
// Gerar um input do tipo hidden que albergará a nossa
// senha nonce.
// Este trecho de código poderia hipoteticamente servir
// num plugin que renomeie posts
?>
<form action="" method="post">
  <?php wp_nonce_field( 'ewp_post_rename_' . $post->ID ); ?>
  <input type="hidden" name="post_id" value="<?php echo $post->ID; ?>" />
  <input type="hidden" name="action" value="rename" />
  <input type="submit" value="Renomear" />
</form>

Esta função, tal como a sua função irmã, recebe como parâmetro um identificado único que diz respeitante à ação do formulário.

Verificar se um senha nonce está correcta:

Esta é a parte que realmente importa. Aqui iremos verificar através da função check_admin_referer() se a senha fornecida existe na base de dados de transients e se corrobora as obrigações descritas em cima.

No código seguinte, vou mostrar qual deve ser a implementação de segurança para um formulário igual ao que foi mostrado em cima:

<?php
// Vamos testar se o usuário atual tem permissões
// de gestão das configurações da instalação do WP
if ( !current_user_can( 'manage_options' ) )
  wp_die( 'Privilégios insuficientes!' );

// Vamos testar se a ação é legítima, ou seja,
// seja proveniente do WordPress
$action = $_REQUEST['action'];
$id = $_REQUEST['post_id'];
check_admin_referer( 'ewp_post_'.$action.'_'.$id );
// Código do seu plugin

?>

Basicamente, este código primeiramente testa se o usuário tem permissão de acesso a estas configurações através da função current_user_can(), e, caso não tenha é gerado um erro do WordPress, e todo o script pára de correr nesse momento. Isto é conseguido com a função wp_die().

Na segunda parte do código, avaliamos se a ação pedida é legítima, ou seja, é proveniente do próprio WordPress e não de um link externo de alguém mal intensionado. Desta maneira garantimos que a segurança necessária para o nosso plugin.

Nós preparámos um documento PDF que contém as capacidades mais comuns associadas a um papel. Poderá fazer o download desse documento para sua referência

Faça o Download do documento de Capacidades e Papeis para WordPress

E aqui fica a versão em PNG:

VALIDAÇÃO E SANEAMENTO DOS DADOS POSTADOS

Depois de vermos dois principais tipos de ataque mais comuns que as comunidades blogueiras temem, existe um outro, que entre todos, é aquele que provavelmente o aluno deve ter mais conhecimento: ataque de força bruta.

Anteriormente vimos ataques de usuários mal intensionados, que tentam ganhar privilégios no sistema WordPress. Neste caso estamos a falar de um nível superior de ataque a que qualquer servidor está sujeito se não forem implementadas as medidas correctas.

Na anatomia deste ataque podemos destacar o facto de um visitante qualquer usar os campos de formulários para tentar injetar código SQL ou base64, de maneira a apoderar-se do próprio sistema operativo ou furtar informação legítima e proprietária. O WordPress em si já detém de um sistema de proteção contra este tipo de ataques, pois como já vimos na série anterior, para aceder ao banco de dados MySQL este usa o wrapper wpdb, no entanto devemos estar sempre cientes que devemos proteger os nossos formulários contra ataques de força bruta.

Exemplos de ataque de força bruta: No formulário de login, no campo password visitante digita um dos seguintes códigos:

  1. qwerty123` OR 1 = 1
  2. base64(‘fe2fjweo923fijoe9vwev924gjljvw9s0ierf2jp4vlkw…..’);
  3. qwerty” /> Password: <input name=”password” value=”12345″

O que acabou de acontecer aqui?

Se qualquer um destes códigos fosse colocado num formulário do seu blogue ou site, você teria sido vítima de um ataque de força bruta. No primeiro exemplo, o visitante tenta ganhar acesso ao formulário de login injetando código SQL. A ideia é muito simples: 1 = 1, logo será sempre verdade mesmo que o utilizador e a password não estejam corretos. O usuário teria ganho permissão para entrar no sistema. No segundo exemplo o usuário usou um código PHP codificado em base64, que poderia correr no ato de login. No terceiro caso o usuário usa HTML para tentar enganar o sistema, apresentando um novo input password.

Qualquer uma destes ataques deve ser ponderado quando construimos um sistema que processa os nossos formulários.

O seguinte código usa o switch como meio de prever estas situações. Assim caso o campo action não seja igual a uma dessas ações, então não faz nada:

<?php
// Usamos o switch como primeiro meio de proteção
// caso o input tenha de ser uma range de variáveis
switch( $_POST['action'] ) {

  case 'rename' :
    // corre o código para renomear o post
    break;

  case 'trash' :
    // corre o código para remover o post
    break;

  default :
    // Se isto acontece então a ação não corresponde
    // ao que está mapeado, o que pode constituir
    // um ataque de força bruta.
    wp_die( 'A ação não se encontra mapeada.' );
    break;

}
?>

Esta é a primeira maneira de nos protegermos: se uma variável deverá ter apenas uma range de valores, então mapeamos os valores e forçamos que isso aconteça.

Primeiro Validar no contexto:

Que é que isto quer dizer? Um input tem um contexto, ou seja, o tipo de valor que se está à espera nesse input. Então, vamos recebê-lo presumindo que o valor que o usuário lá colocou é desse tipo. Caso isso não aconteça será mais fácil descartar os valores. O WordPress e o PHP têm várias funções que nos fazem esse trabalho.

Valores Inteiros:

Enquanto que a função is_int() avalia se o input é um número inteiro retornando true ou false, a função intval() retorna o valor desse input como um valor inteiro, caso seja convertível. A função absint() retorna o valor absoluto, ou seja, não recebe inteiros negativos.

<?php
// Vamos analisar o input em que o usuário deverá
// colocar a sua idade.
// Analisamos se o input é um inteiro
if ( !is_int( $_POST['idade'] )
  wp_die( 'Deverá colocar uma idade correta' );

// Como deve ser um inteiro
// positivo, usamos a função absint().
$idade = absint( $_POST['idade'] );

?>

Textos arbitrários (strings):

No caso do valor esperado ser um texto arbitrário, como um nome, número de celular ou outro caso em que a complexidade do texto não nos permite avaliar mais especificamente que tipo de valor se trata, usamos a função wp_strip_all_tags():

<?php
// Este valor é uma string
$nome = wp_strip_all_tags( $_POST['nome'] );

Esta função elimina qualquer tag HTML, CSS, XML do seu input. É a função ideal para sanear inputs. Formulários com este sistema de proteção não se têm de preocupar mais com ataques de força bruta.

No entanto, há um tipo de ataque que merece uma firewall mais robusta: base64. Nós preparámos um plugin especial que atual como uma firewall para o seu sistema todo. Poderá fazer o download no link que se encontra no final do artigo.

Validar HTML:

O WordPress tem uma função que faz o saneamento de um input com texto HTML, removendo qualquer código malicioso porém mantendo as tags HTML. A função usada é wp_kses():

<?php
// Lista de tags HTML aceites e seus atributos
$tags_aceites = array(
  'p' => array(),
  'strong' => array(),
  'a' => array(
     'href' => array(),
     'title' => array(),
  )
);

// Vamos limpar o input HTML
$html = wp_kses( $_POST['html'], $tags_aceites );

?>

Esta função aceita como parâmetros o HTML em causa e um array de tags e seus atributos que nós aceitamos neste HTML. Qualquer tag que não estejam dentro deste array será removida.

FORMATANDO ELEMENTOS PARA USO DE SQL

Vimos no artigo anterior quais os métodos para aceder ao banco de dados, através da classe WPDB. Vamos aprender mais um método, este protege o código SQL contra qualquer tipo de SQL num input.

<?php
// Função para remover o post
function remover_post() {
  global $wpdb;

  // Este SQL tem um placeholder que aceita um inteiro %d
  $sql = "DELETE FROM {$wpdb->prefix}_posts WHERE id = %d;";

  // Este é o ID do post que queremos remover
  $id = absint( $_POST['id'] );

  // O método prepare faz a limpeza do SQL.
  // Recebe o SQL com o placeholder e o $id que
  // substituirá o %d no código SQL.
  $sql = $wpdb->prepare( $sql, $id );

  // Vamos então correr o SQL seguro
  $wpdb->get_results( $sql );

}

?>

O método prepare recebe o número de parâmetros iguais ao número de placeholders que existirem no código SQL. Com este sistema de proteção acabámos de excluir a última hipótese de ataque ao servidor: ataque por força bruta com SQL.

HÁBITOS ACONSELHADOS

  1. Sempre que criar um plugin, tente encontrar brechas de segurança. Pense em formas de como é que um usuário malicioso poderia atuar e experimente. Considere também colocar-se no papel de um usuário leigo em informática, e que tipos de uso errados poderia ele dar ao seu plugin.
  2. Sempre que projetar um plugin tenha em consideração a segurança desde o inicio do projeto.
  3. Mantenha sempre o seu plugin atualizado com as novas versões do WordPress.
  4. Comente sempre o seu código. Assim sempre que tiver que o rever será mais fácil entender o que determinada função ou parte do código fará. Na documentação do código use sempre palavras chave como TODO ou TOFIX para indicar melhoramentos a serem feitos nas próximas versões.
  5. Tente sempre ser paragmático quando escrever código e focar-se na lógica do problema.
  6. Prefira decisões em vez de opções. Assim estará a diminuir o número de variáveis com que se tem que preocupar.
  7. Divida o seu código em funções. Fica mais organizado e será mais fácil mantê-lo futuramente.

PLUGIN FIREWALL PARA WORDPRESS

Falamos de todos os ataques possíveis e resolvemos todos menos um: ataque com código codificado em base64. Como este tipo de ataque é muito genérico e normalmente ocorre via HTTP ou GET, a solução passa por criar um plugin que atua como firewall.

Nós, da Escola WordPress, criamos um plugin pequeno, muito simples mas bastante robusto que bloqueia qualquer pedido URL que tenha um comprimento exageradamente grande ou que contenha algumas palavras que indiciem que seja um ataque: base64, UNION, JOIN, etc.

Faça Download do Plugin Firewall para WordPress

Instalação:

  1. Faça o upload ou instale a partir do menu “Adicionar Plugin”
  2. Após a instalação, ative o plugin.
Não é necessário fazer mais nada, a partir de agora já se encontra protegido contra ataques via GET e HTTP.

NOTAS FINAIS

Escrever código para WordPress não é dificil, e interligar código já feito muito menos. No entanto deverá ter sempre em mente que, como qualquer bom software, irá sempre encontrar vulnerabilidades no código. Assim, é sempre necessário estar atento a problemas que possam surgir.

Espero que tenham gostado desta série e que vos tenha sido útil.

Até breve,

Vitor