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 WordPress: Nã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 🙂