Per offrirti un'esperienza di navigazione sempre migliore, questo sito utilizza cookie propri e di terze parti, partner selezionati. I cookie di terze parti potranno anche essere di profilazione.
Le tue preferenze si applicheranno solo a questo sito web. Puoi modificare le tue preferenze in qualsiasi momento ritornando su questo sito o consultando la nostra informativa sulla riservatezza.
E' possibile rivedere la nostra privacy policy cliccando qui e la nostra cookie policy cliccando qui.
Per modificare le impostazioni dei cookies clicca qui
  • seguici su feed rss
  • seguici su twitter
  • seguici su linkedin
  • seguici su facebook
  • cerca

SEI GIA' REGISTRATO? EFFETTUA ADESSO IL LOGIN.



ricordami per 365 giorni

HAI DIMENTICATO LA PASSWORD? CLICCA QUI

NON SEI ANCORA REGISTRATO ? CLICCA QUI E REGISTRATI !

Come prevenire attacchi di tipo CSRF in un sito già esistente, in PHP: parte 2

24 luglio 2024
Come prevenire attacchi di tipo CSRF in un sito già esistente, in PHP: parte 2

Nell'articolo precedente ci siamo occupati di come proteggere un sito dagli attacchi di tipo Cross-Site Request Forgery (CSRF), implementando un Token.

Se non l'hai già letto, fallo adesso perchè è alla base di quanto di dirò oggi e qui andrò solo ad integrare quanto ci occorre.

Nell'esempio avevamo un form, come questo

 <form action="action1.php" method="POST">
  <input type="hidden" id="param1" name="param1" value="pippo">
  <input type="hidden" id="param2" name="param2" value="pluto">
  <input type="submit" value="invia primo form">
</form> 

ed una chiamata POST, tramite javascript ed in AJAX, che "posta" tutti i campi del form, e di seguito ne riporto un estratto:

<script>
.........
function handleforms(forms) {
      .......
      // url to call
      var urlToCall = form.action
      // params to post
      var formData = new FormData(this); 
      // append csrf
      formData.append("csrf", csrf);   
      // execute ajax call
      $.ajax({
        url: urlToCall,
        type: "POST",
        data: formData,
       ..................	
      });	
      .......
}
.........
</script>

Infatti "var formData = new FormData(this);" preleva tutti i campi del form con i relativi valori, e li posta così come sono.

Tuttavia capita spesso di non voler postare tutti i campi, o di postare i campi con nomi diversi dai nomi dei campi presenti nel form, o campi aggiuntivi rispetto a quelli presenti nel form.

L'articolo precedente non copre questa casistica, per cui eccoci nuovamente qui per integrare quanto già spiegato.

Facciamo un nuovo esempio: in questa pagina basica abbiamo un form e, incluso nel footer, un file javascript chiamato "index.js" che gestirà la post via ajax.

<!DOCTYPE html>
<html lang='it'> 
<head>
<meta charset='utf-8' />
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'>
</head>
<body>

<h2 style='text-align:center'>LA MIA PAGINA</p>
<br>

 <form action="action1.php">
  <input type="hidden" id="param1" name="param1" value="pippo">
  <input type="hidden" id="param2" name="param2" value="pluto">
  <input type="submit" value="invia primo form" class="bottone">
</form> 

<script src="https://code.jquery.com/jquery-3.7.1.min.js"  integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script type='text/javascript' src='index.js'></script>
</body>

</html>

Piccole note rispetto all'esempio dell'articolo precedente:

  1. nel form non ho indicato "method=POST".. tanto quel submit verrà intercettato da javascript e postato via ajax, per cui non ha senso indicarlo, ma nulla ti vieta di metterlo, è semplicemente inutile.
  2. ho aggiunto una classe "bottone" al pulsante sumbit del form, perchè nel file index.js voglio intercettare il click su questo pulsante.

Questo è il contenuto del file "index.js":

$(document).ready(function() {

   // al click
   $(document).on("click",".bottone",function(event) {

    // blocco la submit
    event.preventDefault();
	
    // recupero la action url da chiamare
   	var form = $(this).closest('form');
    var urlToCall= form.attr('action');

	// recupero i valori dei campi da postare
	var name = document.getElementById('param1').value;
	var surname = document.getElementById('param2').value;
	var id = 4;

	// preparo i campi da postare via ajax
	var form_data = new FormData();
	form_data.append('name',name);
	form_data.append('surname',surname);
	form_data.append('id',id);

    // effettuo la chiamata post
	$.ajax({
	  url: urlToCall,
	  type: "POST",
	  data: form_data,
	  processData: false,
	  contentType: false,
	  async: true,
          cache: false,  
          success: function(data)
          {
            alert(data)
          },
          error: function(xhr, ajaxOptions, thrownError){			
            alert("si è verificato un problema tecnico")
          }				
	});	
   });
});

Al click sul pulsante di submit del form, il javascript intercetta il click, ed "event.preventDefault();" impedisce di eseguire la normale post, che prevederebbe il cambio di pagina: sarà ajax infatti a fare la chiamata e ricevere la risposta.

Come possiamo vedere, sto effettuando una chiamata POST, ma non passo "param1" bensì il campo "name", e non passo "param2" ma "surname", ed inoltre ho aggiunto un campo "id" che non era presente tra i campi del form.

Nell'articolo precedente avevamo visto come implementare un sistema che si applicasse automaticamente a tutti i form, ma questo prevedeva che postassimo tutti i campi del form, senza selezionarli, senza cambiarne il nome, senza aggiunta di nuovi parametri, come in questo esempio.

Da questo nasce l'esigenza, anche grazie alle vostre segnalazioni, di creare una seconda parte dell'articolo sul CSRF, per andare a coprire davvero tutti i casi, come questo.

In questo javascript "index.js", non dobbiamo fare alcuna modifica.

Quello che dobbiamo fare è, come fatto nel precedente articolo

  1. includere nella pagina la config.php che a sua volta chiama un metodo che crea (e verifica) il token
  2. includere nella pagina un meta tag con valore il token generato
  3. includere nel footer un javascript da usare globalmente, in tutte le pagine del sito, che chiamiamo "global.js": sarà questo a fare la magia e aggiungere a tutte le ajax post, automaticamente, il token, in qualunque pagina siano i form (potrei anche avere 100 form nella stessa pagina o in varie pagine del sito!)

Ci riportiamo quindi in una situazione simile a quella del precedente articolo

<?php
include "config.php";
?>

<!DOCTYPE html>
<html lang='it'> 
<head>
<meta charset='utf-8' />
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'>
<meta name="_csrf" content="<?=$token?>"/>
</head>
<body>

<h2 style='text-align:center'>LA MIA PAGINA</p>
<br>

 <form action="action1.php">
  <input type="hidden" id="param1" name="param1" value="pippo">
  <input type="hidden" id="param2" name="param2" value="pluto">
  <input type="submit" value="invia primo form" class="bottone">
</form> 

<script src="https://code.jquery.com/jquery-3.7.1.min.js"  integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script type='text/javascript' src='index.js'></script>
<script type='text/javascript' src='global.js'></script>
</body>

</html>

Vediamo adesso il contenuto del file "global.js" che varierà rispetto a quello dell'articolo precedente: in questo caso infatti non dobbiamo intercettare le post fatta dai tag <form>, bensì quelle fatte dalle chiamate ajax.

Usando jQuery:

$.ajaxSetup({
  beforeSend: function(jqXHR, settings) {
    // Check if the request is a POST request
    if (settings.type === 'POST') {
        // prelevo il csrf
        const csrf=document.querySelector('meta[name="_csrf"]').getAttribute('content');

        // if settings.data is a string, add the new parameter csrf to the data
		if (typeof settings.data === 'string') {
			settings.data += '&csrf='+csrf;
		} 
		// if settings.data is an object, extend it with the new parameter csrf
		else {
			settings.data.append('csrf', csrf);
		}
    }
  }
});

Lo script lavora così: prima di eseguire una qualunque chiamata AJAX ("beforeSend")

  1. viene verificata se la ajax è una chiamata di tipo POST
  2. se è una POST, viene prelevato il valore token CSRF dal meta tag ed aggiunto ai parametri della richiesta.

In questo modo possiamo automaticamente aggiungere ad ogni ajax post, in qualunque pagina venga effettuata, il nostro token!

Abbiamo così coperto tutte le possibili casistiche che l'articolo precedente non copriva.

Puoi scaricare il sorgente da GitHub. Il link subito sotto.

Approfondimenti

Potrebbe interessarti

 
 
 
 
pay per script

Hai bisogno di uno script PHP personalizzato, di una particolare configurazione su Linux, di una gestione dei tuoi server Linux, o di una consulenza per il tuo progetto?

x

ATTENZIONE