Última modificação em 10 de janeiro de 2021.

O ataque Cross-Site Request Forgery (CSRF) é um abuso de segurança comum que ocorre em toda a web. Proteger o servidor contra esse ataque é um mecanismo de proteção de primeiro nível para proteger o seu site.

Usuários mal-intencionados da Internet costumavam clonar solicitações para atacar servidores vulneráveis. Essa clonagem pode acontecer incorporando o link do site malicioso à página da web do usuário.

A implementação de anti-CSRF reduz a vulnerabilidade do site. Com essa proteção, o site rejeita o acesso malicioso que envia solicitações sem token CSRF ou errado.

O diagrama a seguir mostra a validação da solicitação do usuário contra o ataque CSRF. Se um usuário genuíno postar o formulário com o token apropriado, o servidor processa a solicitação. Caso contrário, rejeita, na ausência do parâmetro de token CSRF.

Manuseio de formulários com proteção anti-CSRF

Veremos um exemplo de código formulário de contato em PHP com proteção CSRF neste tutorial. Com essa proteção, ele garante a autenticidade da solicitação antes de processá-la.

Criei um serviço em PHP para lidar com a validação de segurança contra o ataque CSRF. O servidor rejeitará as solicitações dos usuários que não tenham token ou token inválido.

Se você deseja ter um formulário de contato com proteção CSRF e mais recursos de segurança, obtenha o Iris .

O que há dentro?

  1. Sobre este exemplo
  2. Gerar token CSRF e criar sessão PHP
  3. Renderizar o formulário de contato com o token CSRF
  4. Validação de Anti Cross-Site Request Forgery (CSRF) em PHP
  5. Serviço de segurança para gerar, inserir e validar o token CSRF
  6. Resultado: resposta de validação CSRF do servidor

Sobre este exemplo

Este código implementa a proteção Anti CSRF em um formulário de contato do PHP . Ele processa um formulário de contato. Os gerenciadores de postagem deste formulário validam as solicitações do usuário contra o ataque CSRF.

Ao carregar a página de destino, o script PHP gera o token CSRF. O rodapé do formulário terá este token como um campo oculto. Além disso, ele gerencia o token em uma sessão PHP.

Ao postar os campos do formulário, o código PHP verificará o parâmetro do token CSRF. Se encontrado, ele valida o token da sessão.

Se o usuário enviar uma solicitação sem um token CSRF, o servidor rejeitará a solicitação. Além disso, se o token não corresponder ao token da sessão, o servidor rejeitará a solicitação.

Com a validação do token CSRF bem-sucedida, ele enviará o e-mail de contato para o endereço de destino. O diagrama a seguir mostra a estrutura do arquivo deste exemplo.

Anti CSRF Token Code File Structure

Gerar token CSRF e criar sessão PHP

Em uma página inicial, o script de rodapé do formulário invoca SecurityService. Esta é uma classe PHP para gerar um token CSRF.

Ele grava o token em uma sessão PHP para referência futura. Isso ajudará na hora de processar a validação do CSRF após a postagem do formulário.

O rodapé do formulário é um arquivo de estrutura que carrega o token gerado em um campo oculto.

O snippet de código abaixo é do SecurityService.php para gerar o toke CSRF. O código completo da classe de serviço é mostrado na próxima seção deste artigo.

SecurityService.php (código para gerar token CSRF)

/** * Gerar, armazenar e retornar o token CSRF * * @return string [] */ public function getCSRFToken () { if (vazio ($ this-> sessão [$ this-> sessionTokenLabel])) { $ this-> sessão [$ this-> sessionTokenLabel]=bin2hex (openssl_random_pseudo_bytes (32)); } if ($ this-> hmac_ip!==false) { $ token=$ this-> hMacWithIp ($ this-> sessão [$ this-> sessionTokenLabel]); } outro { $ token=$ this-> sessão [$ this-> sessionTokenLabel]; } return $ token; }

Este é um formulário de contato HTML com os campos usuais nome, e-mail, assunto e mensagem. Além disso, há um campo oculto csrf-token com o token gerado.

A ação de envio processa validação do formulário jQuery antes de postar os parâmetros no PHP.

O script de validação do lado do cliente lida com a validação básica no envio. Ele aplica a verificação de não vazio em cada campo.

index.php (modelo HTML)

 

 Proteção contra CSRF usando PHP 





Proteção contra CSRF usando PHP

Nome
Envie um email para
Assunto
Mensagem

Este é o script de rodapé do formulário que aciona o manipulador de serviço para gerar tokens. O insertHiddenToken () grava o código HTML para carregar o campo do token csrf no formulário.

view/framework/form-footer.php

  insertHiddenToken ();

assets/js/validation.js

 function validateContactForm () {
var válido=verdadeiro;
$ ("# userName"). removeClass ("campo de erro");
$ ("# userEmail"). removeClass ("error-field");
$ ("# assunto"). removeClass ("campo de erro");
$ ("# content"). removeClass ("campo de erro"); $ ("# userName-info"). html (""). hide ();
$ ("# userEmail-info"). html (""). hide ();
$ ("# subject-info"). html (""). hide ();
$ ("# content-info"). html (""). hide (); $ (". mensagem de validação"). html ("");
$ (". phppot-input"). css ('border','# e0dfdf 1px solid'); var userName=$ ("# userName"). val ();
var userEmail=$ ("# userEmail"). val ();
var subject=$ ("# assunto"). val ();
var content=$ ("# content"). val (); if (userName.trim ()=="") {
$ ("# userName-info"). html ("obrigatório."). css ("color","# ee0000"). show ();
$ ("# userName"). css ('border','# e66262 1px solid');
$ ("# userName"). addClass ("campo de erro"); válido=falso;
}
if (userEmail.trim ()=="") {
$ ("# userEmail-info"). html ("obrigatório."). css ("color","# ee0000"). show ();
$ ("# userEmail"). css ('border','# e66262 1px solid');
$ ("# userEmail"). addClass ("error-field"); válido=falso;
}
if (! userEmail.match (/^ ([\ w-\.] + @ ([\ w-] + \.) + [\ w-] {2,4})? $/)) {
$ ("# userEmail-info"). html ("endereço de e-mail inválido."). css ("color", "# ee0000"). show (); $ ("# userEmail"). css ('border','# e66262 1px solid');
$ ("# userEmail"). addClass ("error-field"); válido=falso;
} if (assunto=="") {
$ ("# subject-info"). html ("obrigatório."). css ("color","# ee0000"). show ();
$ ("# assunto"). css ('borda','# e66262 1px sólido');
$ ("# assunto"). addClass ("campo de erro"); válido=falso;
}
if (content=="") {
$ ("# userMessage-info"). html ("obrigatório."). css ("color","# ee0000"). show ();
$ ("# content"). css ('border','# e66262 1px solid');
$ ("# content"). addClass ("campo de erro"); válido=falso;
} if (válido==falso) {
$ ('. campo de erro'). first (). focus ();
válido=falso;
}
retorno válido;
}

Validação Anti Cross-Site Request Forgery (CSRF) em PHP

Ao enviar o formulário de contato incorporado ao token, a ação do formulário executa o seguinte script.

A função validate () do SecuritySercive compara o token postado com aquele armazenado na sessão.

Se uma correspondência for encontrada, ele continuará enviando o e-mail de contato. Caso contrário, ele reconhecerá o usuário com uma mensagem de erro.

index.php (validação de PHP CSRF e tratamento de formulários)

  validate (); if (! empty ($ csrfResponse)) { require_once __DIR__.'/lib/MailService.php'; $ mailService=new MailService (); $ response=$ mailService-> sendContactMail ($ _ POST); if (! vazio ($ resposta)) { $ message="Olá, recebemos sua mensagem. Obrigado."; $ type="sucesso"; } outro { $ message="Incapaz de enviar email."; $ type="erro"; } } outro { $ message="Alerta de segurança: Incapaz de processar o seu pedido."; $ type="erro"; }
} ?>

Serviço de segurança para gerar, inserir, validar token CSRF

Esta classe de serviço criada em PHP inclui métodos para processar as operações relacionadas à proteção CSRF.

Ele define uma propriedade de classe para definir o nome do campo do token do formulário e o índice da sessão.

Possui métodos para gerar tokens e gravá-los no HTML e em uma sessão de PHP.

Ele usa mitigações XSS ao escrever o rodapé do formulário com o token.

Além disso, tem a opção de excluir alguns URLs do processo de validação. Os URLs excluídos ignoram o processo de validação CSRF.

O código obtém o URL da solicitação atual das variáveis ​​PHP SERVER. Em seguida, ele o compara com a matriz de URLs excluídos para pular a validação.

lib/SecurityService.php

  excludeUrl=$ excludeUrl; } if (! \ is_null ($ post)) { $ this-> post=& $ post; } outro { $ this-> post=& $ _POST; } if (! \ is_null ($ server)) { $ this-> server=& $ server; } outro { $ this-> server=& $ _SERVER; } if (! \ is_null ($ sessão)) { $ this-> sessão=& $ sessão; } elseif (! \ is_null ($ _ SESSION) && isset ($ _ SESSION)) { $ this-> sessão=& $ _SESSION; } outro { lançar new \ Error ('Nenhuma sessão disponível para persistência'); } } /** * Insira um token CSRF em um formulário * * @param string $ lockTo * Este token CSRF é válido apenas para este endpoint de solicitação HTTP * @param bool $ echo * se verdadeiro, echo em vez de retornar * @return string */ public function insertHiddenToken () { $ csrfToken=$ this-> getCSRFToken (); echo"  xssafe ($ this-> formTokenLabel)."\""."value=\"". $ this-> xssafe ($ csrfToken)."\""."/>"; } //xss funções de mitigação função pública xssafe ($ data, $ encoding='UTF-8') { return htmlspecialchars ($ data, ENT_QUOTES | ENT_HTML401, $ encoding); } /** * Gerar, armazenar e retornar o token CSRF * * @return string [] */ public function getCSRFToken () { if (vazio ($ this-> sessão [$ this-> sessionTokenLabel])) { $ this-> sessão [$ this-> sessionTokenLabel]=bin2hex (openssl_random_pseudo_bytes (32)); } if ($ this-> hmac_ip!==false) { $ token=$ this-> hMacWithIp ($ this-> sessão [$ this-> sessionTokenLabel]); } outro { $ token=$ this-> sessão [$ this-> sessionTokenLabel]; } return $ token; } /** * hashing com endereço IP removido para facilitar a conformidade com o GDPR * e hmacdata é usado. * * @param string $ token * @return string dados hash */ função privada hMacWithIp ($ token) { $ hashHmac=\ hash_hmac ($ this-> hashAlgo, $ this-> hmacData, $ token); return $ hashHmac; } /** * retorna o URL da solicitação atual * * @return string */ função privada getCurrentRequestUrl () { $ protocolo="http"; if (isset ($ this-> servidor ['HTTPS'])) { $ protocolo="https"; } $ currentUrl=$ protocolo."://". $ this-> servidor ['HTTP_HOST']. $ this-> servidor ['REQUEST_URI']; return $ currentUrl; } /** * função principal que valida a tentativa de CSRF. * * @throws \ Exception */ public function validate () { $ currentUrl=$ this-> getCurrentRequestUrl (); if (! in_array ($ currentUrl, $ this-> excludeUrl)) { if (! empty ($ this-> post)) { $ isAntiCSRF=$ this-> validateRequest (); if (! $ isAntiCSRF) { //tentativa de ataque CSRF //tentativa de CSRF é detectada. Não precisa revelar essa informação //para o invasor, então falhando sem informações. //Código de erro 1837 significa tentativa CSRF e isso é para //nossos objetivos de identificação. retorna falso; } return true; } } } /** * a validação real do CSRF acontece aqui e retorna booleano * * @return boolean */ public function isValidRequest () { $ isValid=false; $ currentUrl=$ this-> getCurrentRequestUrl (); if (! in_array ($ currentUrl, $ this-> excludeUrl)) { if (! empty ($ this-> post)) { $ isValid=$ this-> validateRequest (); } } return $ isValid; } /** * Valide uma solicitação com base na sessão * * @return bool */ public function validateRequest () { if (! isset ($ this-> sessão [$ this-> sessionTokenLabel])) { //Token CSRF não encontrado retorna falso; } if (! empty ($ this-> post [$ this-> formTokenLabel])) { //Vamos extrair os dados POST $ token=$ this-> post [$ this-> formTokenLabel]; } outro { retorna falso; } if (! \ is_string ($ token)) { retorna falso; } //Pegue o token armazenado if ($ this-> hmac_ip!==false) { $ esperado=$ this-> hMacWithIp ($ this-> sessão [$ this-> sessionTokenLabel]); } outro { $ esperado=$ this-> sessão [$ this-> sessionTokenLabel]; } return \ hash_equals ($ token, $ esperado); } /** * remove o token da sessão */ public function unsetToken () { if (! empty ($ this-> sessão [$ this-> sessionTokenLabel])) { não definido ($ this-> sessão [$ this-> sessionTokenLabel]); } }
}

Este MailService.php usa a função mail () do núcleo do PHP para enviar os e-mails de contato. Você pode substituí-lo pelo SMTP via script de envio de e-mail.

lib/MailService.php

 

Resultado: Resposta de validação CSRF do servidor

A imagem mostra o formulário de contato usual abaixo. Já vimos essa saída em muitos tutoriais de formulários de contato antes.

Abaixo da interface do formulário, esta imagem mostra a mensagem de alerta de segurança em vermelho. Ele reconhece os usuários que enviam solicitações com o token errado ou vazio.

Anti CSRF Protection Output

Conclusão

Assim, implementamos a proteção anti-CSRF em um formulário de contato do PHP.

Espero que o código de exemplo seja útil e você obtenha o processo de implementação que discutimos aqui.

Criamos uma classe SecurityService em PHP para lidar com a proteção CSRF. É reutilizável para vários aplicativos onde quer que você precise ativar a proteção CSRF.

O código PHP que retorna mensagens de resposta reconhece o usuário adequadamente.

Download

↑ Voltar ao topo

Source link

Categories: Wordpress