Adicionando Segurança no seu Tema

Adicionando Segurança no seu Tema

Assim como em qualquer aplicação que você construir, seja ela web ou mobile, um dos assuntos mais importantes e cruciais que qualquer desenvolvedor precisa ter, é o mínimo de conhecimento sobre SEGURANÇA.

A segurança não só engloba questões como SQL INJECTION ou XSS INJECTION, ela vai muito além disso, passando também por processos de validação de dados, técnicas anti-spam, chegando até a proteção dos dados do seu usuário.

Proteger sua aplicação contra possíveis invasões é ótimo, mas garantir que as informações do seu usuário estejam 99,9% seguras, é melhor ainda 🤓

No universo de plugins do WordPress, quando pensamos em segurança, alguns de nós se lembram de um dos plugins mais famosos desse meio, que é o WordFence.

E eu devo dizer que o WordFence é um ótimo aliado em questões de segurança do WordPress, entretanto, você deve entender que ele é apenas mais um plugin, que pode ser instalado ou não pelo usuário do seu tema.

E como você já deve saber, plugin é plugin, tema é tema!

Um plugin pode tapar possíveis furos de segurança que podem existir em alguns temas do WordPress? Sim, e muitos deles foram construídos visando solucionar esses problemas.

Confesso que é até desleixo da parte do desenvolvedor, criar temas para WordPress ignorando a segurança dos mesmos, de modo a precisar indicar plugins de segurança para seus clientes, para aquilo que o próprio desenvolvedor poderia resolver 🤡

Porém, como bons desenvolvedores de temas que nós somos, não queremos dar esse mole não é verdade? 😋

Até porque, nós buscamos criar aplicações 99,9% seguras para os nossos usuários, garantindo que eles não só disponham de uma página bonita e veloz, como também de uma página segura, tanto para o administrador do tema, quanto para os visitantes que acessam o site.

Entrada e Saída de dados (Input/Output)

Toda aplicação web (e mobile também), possuem em sua essência a mesma estrutura de envio e recebimento de informações, estrutura essa muito difundida no meio da computação, chamada de: Entrada e Saída de dados.

Como o próprio nome já nos diz, entrada de dados é toda e qualquer interação que o usuário realiza dentro da aplicação, e isso incluí eventos como: cliques, movimentos do mouse, pressionamento de teclas, abertura de links, upload de arquivos, requisições HTTP enviadas pelo navegador, e envio de formulários em geral.

Todos esses dados, podem ser recebidos e tratados pelo servidor onde se encontra hospedada a sua aplicação.

Quando falamos de saída de dados, é toda apresentação que o servidor da web entrega para o usuário, e isso incluí renderização de elementos HTML, fornecimento de estilos CSS e scripts Javascript, além do fornecimento de dados estruturados como XML, JSON e entre outros.

Ou seja, a entrada de dados é tudo aquilo que fazemos que é percebido pelo sistema, já a saída de dados é tudo o que o sistema faz que é percebido pela gente (usuários).

Sabendo disso, é importante que saibamos utilizar algumas técnicas aplicadas ao escopo de segurança do WordPress.

Técnicas de segurança para entrada de dados

Durante a jornada, você chegou a aplicar alguns conceitos de entrada de dados, sendo uma delas a criação de formulários avulsos (caso você tenha construído um), além dos controles do Theme Customizer.

Você pode se perguntar"Formulários avulsos eu até entendo, mas os controles do Theme Customizer? Mas porque um administrador vai querer "hackear" seu próprio sistema?"

E eu respondo: Meu amigo(a) hoje em dia não podemos confiar nem no próprio usuário 😂

De acordo com a documentação do WordPressNão confie em nenhum dado. Não confie na entrada do usuário, em APIs de terceiros ou nos dados do seu banco de dados sem verificação. A proteção de seus temas WordPress começa garantindo que os dados que entram e saem de seu tema sejam conforme o esperado. Sempre certifique-se de validar e higienizar a entrada do usuário antes de usá-la e de escapar na saída.

Voltando ao assunto, acredito que você já saiba, que alguns formulários do próprio HTML5 contam com algumas tags que podem adicionar "um certo nível de segurança" a nossas aplicações, são elas:

  • required (para garantir que o usuário não envie o campo em branco)
  • type (para garantir que o usuário escreva o tipo de dado certo no campo certo)
  • pattern (para garantir que o usuário tenha digitado um certo dado obedecendo um padrão especificado)
  • maxlength (para garantir que o usuário não extrapole o número máximo de caracteres permitidos)
  • minlength (para garantir que o usuário não informe um número menor de caracteres permitidos)

Todavia, essas "validações" acontecem do lado do navegador do cliente, onde eles podem ou não ignora-las, mas como assim?

Vão existir usuários comuns que vão respeitar o formulário, quanto também existirão outros que saberão modificar as informações desses formulários, de modo a gerar dores de cabeça para os administradores do site.

É o famoso usuário que sabe modificar o código Javascript, CSS e HTML por meio do "Inspecionar" do navegador.

Sendo assim, se o servidor da web não estiver preparado para executar uma segunda validação... sinto muito, mas o seu sistema não é seguro!

Isso não excluí o fato de que você não deveria fazer o uso das tags required, pattern, type e entre outras... você deve SIM UTILIZA-LAS, uma vez que elas fazem parte da primeira etapa de validação!

A segunda etapa, envolve revalidar do lado do servidor, todos os dados que foram recebidos.

Por exemplo: Se você adicionou formulários em alguma parte do seu tema, é recomendável que você valide cada um dos campos por meio de classes ou funções do PHP, de modo a garantir que esses dados estejam em conformidade com os padrões estabelecidos dentro das tags de required, type, pattern e afins.

Por exemplo, veja esse <input> que espera receber um e-mail:

<input name="user_email" type="email" class="form-control" placeholder="Seu E-mail" id="userEmail" pattern=".{5,180}" title="Sua [E-mail] deve conter entre 5 a 180 caracteres" maxlength="180" required="">

Perceba que ele não pode ser em branco (required), espera receber um dado do tipo e-mail (type) e que tenha entre 5 a 180 caracteres (pattern, minlength, maxlength).

Do lado do servidor, você vai precisar revalidar se o dado informado pelo seu usuário obedece a esses padrões:

$userEmail = $_POST["user_email"];

if (filter_var($userEmail, FILTER_VALIDATE_EMAIL) && strlen($userEmail) >= 5 && strlen($userEmail) <= 180) {
 echo "Email válido: " . $userEmail;
} else {
 echo "Por favor, forneça um email válido com 5 a 180 caracteres.";
}

O código acima é só um exemplo, e talvez não reflita a lógica implementada por você.

Já no Theme Customizer, você aprendeu um assunto bastante importante chamado de SANITIZE, onde você conheceu algumas funções do próprio WordPress, que limpam os dados informados pelo usuário, de modo a melhorar a segurança da aplicação.

Com relação aos controles, recomendo que você continue seguindo essa prática de sanitize, agora, se você for criar sua própria função de sanitização, lembre-se de garantir que ela é segura, ok?

Por fim, nós temos a terceira etapa que engloba a validação também na saída dos dados.

Você se lembra, quando eu ensinei a conectar algum controle com um arquivo do seu tema durante a lição do Theme Customizer?

Você percebeu que mesmo eu limpando os dados de entrada, eu também limpava os dados de saída?

Onde eu fiz o uso dos comandos como: sanitize_hex_color()esc_html()absint() e entre outros, que eram usados para sanitizar os dados que estavam vindo do banco de dados por meio da função get_theme_mod().

Então, esta é uma ótima estratégia em casos que o banco de dados foi comprometido (invadido, hackeado), ao mesmo tempo que o código fonte do seu site continua intacto.

Dicas de Segurança

Abaixo eu separei duas super dicas que você DEVE aplicar no código fonte do seu tema do WordPress:

Dica 1) Escape os campos de entrada e saída

Como mencionado anteriormente neste artigo, a ideia é que você escape (use comando de sanitização) em todos os campos de entrada, saída e também em traduções de texto.

Dica 2) Existem funções que já são seguras

Algumas funções do WordPress como get_header(), get_footer(), e todas as outras funções que começam com a palavra the, como: the_title(), the_description(), the_content() e entre outras... já são seguras pelo WordPress e não precisam de funções de escape.

Com isso em mente, vamos conhecer algumas funções de sanitização que também poder ser encontradas por meio deste link.

Funções de Sanitização

O WordPress conta atualmente com 21 funções de sanitização disponibilizadas pelo CMS, vamos conhecer cada uma delas.

sanitize_email($email): Remove todos os caracteres ilegais de um endereço de e-mail e valida o formato. Essa função retorna o e-mail sanitizado.

sanitize_file_name($filename): Remove caracteres inválidos de um nome de arquivo, substituindo espaços por traços.

sanitize_hex_color($color): Garante que o valor fornecido seja uma representação válida de uma cor em hexadecimal.

sanitize_hex_color_no_hash($color): Similar a sanitize_hex_color(), mas retorna o valor sem o caractere '#' (hash).

sanitize_html_class($class, $fallback): Garante que o valor fornecido seja seguro para ser usado como uma classe HTML.

sanitize_key($key): Remove caracteres indesejados de uma chave, tornando-a segura para ser usada, por exemplo, como uma chave de array.

sanitize_meta($meta_key, $meta_value, $meta_type): Sanitiza um valor de metadado com base em um tipo específico.

sanitize_mime_type($mime_type): Garante que o tipo MIME fornecido seja seguro para uso.

sanitize_option($option, $value): Sanitiza um valor de opção antes de ser salvo no banco de dados.

sanitize_sql_orderby($orderby): Sanitiza uma cláusula ORDER BY em uma consulta SQL.

sanitize_term($term, $taxonomy, $context = 'display'): Sanitiza um termo de taxonomia antes de ser exibido ou salvo no banco de dados.

sanitize_term_field($field, $value, $term_id, $taxonomy, $context = 'display'): Sanitiza um campo específico de um termo de taxonomia antes de ser exibido ou salvo no banco de dados.

sanitize_text_field($str): Remove caracteres inválidos de uma string, tornando-a segura para exibição ou armazenamento.

sanitize_textarea_field($text): Similar a sanitize_text_field(), mas mantém a formatação de quebras de linha.

sanitize_title($title, $fallback_title, $context): Sanitiza um título antes de ser exibido ou salvo no banco de dados.

sanitize_title_for_query($title): Sanitiza um título para ser usado em uma consulta.

sanitize_title_with_dashes($title): Sanitiza um título, substituindo espaços por traços.

sanitize_user($username, $strict): Sanitiza um nome de usuário, removendo caracteres inválidos.

sanitize_url($url): Garante que uma URL fornecida seja segura para uso.

wp_kses($data, $allowed_html, $allowed_protocols): Filtra o conteúdo HTML com base em uma lista de tags HTML permitidas.

wp_kses_post($data): Filtra o conteúdo HTML de uma postagem com base em uma lista de tags HTML permitidas.

O intuito é exemplificar o nome das funções e o que cada uma faz, atualmente na documentação do WordPress, existem conteúdos que ensinam sua utilização com exemplos do mundo real, vale a pena dar uma olhada.

Trabalhando com as tags HTML permitidas do wp_kses()

É importante que você saiba como funciona o segundo parâmetro da função wp_kses(), que é um tipo de função que você poderá usar bastante durante o desenvolvimento do seu tema.

Como você já deve saber, é possível criar controles que aceitam códigos HTML, e uma das formas de filtrar esses códigos é por meio da função wp_kses(), onde podemos passar um array como segundo parâmetro, de modo a informar quais tags podem ser inseridas.

Observe como isso funciona:

<?php

$allowedTags = array(
 'a' => array(
 'href' => true,
 'title' => true,
 'target' => true,
 ),
 'br' => array(),
 'em' => array(),
 'strong' => array(),
 'span' => array(
 'style' => true,
 'class' => true,
 'id' => true,
 ),
 'div' => array(
 'style' => true,
 'class' => true,
 'id' => true,
 ),
 'p' => array(),
 'ul' => array(),
 'ol' => array(),
 'li' => array(),
 'blockquote' => array(),
 'cite' => array(),
 'code' => array(),
);

echo wp_kses(get_theme_mod('setting_customhtml'), $allowedTags);

De acordo com o array existente na variável $allowedTags, é permitido o uso das tags <a> (em conjunto com os atributos style="", class="" e id=""), <br>, <em>, <strong>, <span> (em conjunto com os atributos style="", class="" e id=""), <div> (em conjunto com os atributos style="", class="" e id=""), <p>, <ul>, <ol>, <li>, <blockquote>, <cite> e <code>.

Toda tag HTML que não faz parte do conjunto listado acima, será removida, como é o caso das tags <script> e entre outras.

O terceiro parâmetro refere-se ao $allowed_protocols, que é um filtro aplicado geralmente em links <a> que aceitam o atributo <href>, como é o caso do exemplo abaixo:

$data = '<a href="http://example.com" onclick="alert(\'Hello!\')">Clique aqui</a>';//exemplo de código HTML

$allowed_html = array(
 'a' => array(
 'href' => true,
 'title' => true,
 ),
);

$allowed_protocols = array('http', 'https');

$clean_data = wp_kses($data, $allowed_html, $allowed_protocols);

O código acima diz que os procotolos aceitos para o link são somente http e https.

Por padrão, ele inclui os protocolos mais comuns, como 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp', 'webcal'.

Validação de dados

O próprio WordPress, recomenda o uso de funções de validação (herdados pelo PHP). Vejamos abaixo algumas delas:

balanceTags( $html ) ou force_balance_tags( $html ): Tenta garantir que as tags HTML estejam balanceadas para que XML válido seja gerado.

count(): para verificar quantos itens existem em um array.

in_array(): para verificar se algo existe em um array.

is_email(): valida se um endereço de e-mail é válido.

is_array(): para verificar se algo é um array.

mb_strlen() ou strlen(): para verificar se uma string tem o número esperado de caracteres.

preg_match(), strpos(): para verificar a ocorrência de determinadas strings em outras strings.

sanitize_html_class( $class, $fallback ): Sanitiza um nome de classe HTML para garantir que contenha apenas caracteres válidos. Remove a string para conter apenas A-Z, a-z, 0-9, '-' e, se isso resultar em uma string vazia, retornará o valor alternativo fornecido.

tag_escape( $html_tag_name ): Sanitiza um nome de tag HTML (não escapa nada, apesar do nome da função).

term_exists(): verifica se uma etiqueta, categoria ou outro termo de taxonomia existe.

username_exists(): verifica se o nome de usuário existe.

validate_file(): irá validar se um caminho de arquivo inserido é um caminho real (mas não se o arquivo realmente existe).

Para saber mais sobre o funcionamento dessas funções, consulte este link.

Funções de Escape

Como dito anteriormente, essas funções são de extrema importância, pois escapar a saída de dados, nada mais é do que o processo de proteger tais dados de forma a eliminar os indesejados, como HTML malformado ou quem sabe tags de script (XSS).

esc_html($value): função responsável por remover qualquer tipo de conteúdo HTML que exista dentro da string armazenada dentro da variável $value.

<h4><?php echo esc_html( $titulo ); ?></h4>

Observação: Caso você queira que a variável $value ou $titulo contenham tags HTML, o esc_html() não é a melhor opção.

esc_js($value): função responsável por garantir que os dados sejam tratados adequadamente para evitar a execução de código JavaScript indesejado.

<script>
 var dados = <?php echo esc_js( $dados ); ?>;
</script>

<div onclick='<?php echo esc_js( $value ); ?>' />

esc_url($value): Função responsável que é usada em incluindo aqueles nos atributos src e href de um elemento HTML.

<img alt="" src="<?php echo esc_url( $media_url ); ?>" />

esc_url_raw($value): Use ao armazenar uma URL no banco de dados ou em outros casos onde são necessárias URLs não codificadas.

esc_xml($value): Use para escapar do bloco XML.

esc_attr($value): Use em tudo o mais que estiver impresso no atributo de um elemento HTML.

<ul class="<?php echo esc_attr( $value ); ?>">

<img src="..." width="<?php echo esc_attr( $width ); ?>" />

esc_textarea(): Use isto para codificar texto para uso dentro de um elemento textarea.

wp_kses(): Use para escapar com segurança de todo HTML não confiável (texto de postagem, texto de comentário, etc.). Isso preserva o HTML.

wp_kses_post(): versão alternativa de wp_kses() que permite automaticamente todo HTML permitido no conteúdo da postagem.

wp_kses_data(): Versão alternativa de wp_kses() que permite apenas o HTML permitido nos comentários da postagem.

Escapando Traduções

É sempre bom escapar traduções, pois existem casos em que alguns tradutores mal intencionados, inserem códigos de ataque dentro das próprias strings de tradução 🤯

esc_html__(): usada para traduzir e escapar uma string antes dela ser atribuída a uma variável ou argumento. Funciona de forma semelhante a função __(), só que atua de forma mais segura.

esc_html__('Texto a ser Traduzido!', 'wp-micilini');

esc_html_e(): usada para traduzir e escapar uma string antes dela ser exibida na página. Funciona de forma semelhante a função _e(), só que atua de forma mais segura:

esc_html_e('Texto a ser Traduzido!', 'wp-micilini');

esc_html_x(): usado quando você tem strings que podem ter diferentes traduções com base em um contexto.

$nome = 'Micilini';
$texto = esc_html_x('Olá, %s!', 'como está?', 'wp-admin');
echo sprintf($texto, esc_html($nome));

esc_attr__(): é semelhante a esc_html__(), mas é projetada especificamente para escapar atributos HTML.

$atributo = esc_attr__('1920', 'wp-micilini');
echo '<input type="text" value="' . $atributo . '">';

esc_attr_e(): funciona da mesma forma que esc_attr__(), mas imprime diretamente, pois executa um echo.

esc_attr_e('1920', 'wp-micilini');

esc_attr_x(): usado para escapar e traduzir atributos HTML com base em um contexto.

$valor = esc_attr_x('1920', 'micilini', 'wp-micilini');

echo '<input type="text" ' . $valor . '>';

Para saber mais sobre os diversos outros tipos de funções seguras, consulte a documentação.

Conclusão

Usar métodos de segurança no seu tema do WordPress é essencial, e é mais uma prova de que você se preocupa com a segurança dos seus usuários 😁

Sendo assim, eu tenho uma nova missão para você:

  • Certifique-se de que você está fazendo o uso das funções de sanitização dentro do functions.php,
  • Certifique-se de que você está escapando todos os dados de saída retornados pelo banco de dados (Tanto na entrada quanto na saída),
  • Certifique-se de que você está escapando todas as funções de tradução.

Feito isso, te aguardo na próxima lição 🙂