Elucubrato da saibal
Addì 22 marzo 2013
Rivisitazione della tecnica “honeypot” per moduli antispam senza captcha – UPDATE
Esempio n° 4
Gli esempi appena proposti non fermano i cosìdetti “Formfills bot”, ovvero quelli che ripetono una procedura registrata.
Anche dando un nome completamente random ai campi di testo (un codice al posto di “email”, “messaggio” etc etc) un tool come Selenium è in grado di individuare gli input da riempire basandosi sulla gerarchia degli elementi. Per risolvere anche questo inconveniente sfruttiamo l’evento “keypress”. Selenium, infatti, non permette l’intercettazione dei tasti sulla tastiera e quindi siamo in grado di distinguere tra un modulo riempito a mano ed uno in automatico.
Codice da inserire in testa alla pagina:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<?php /* * Filename: antispam-form4.php */ session_start(); $salt = 'parola-segreta'; $rchar = chr(97 + mt_rand(0, 25)); $time = time(); $hash_real = md5($salt.$time.$rchar); $hash_fake = md5($salt.($time+rand(1,999)).$rchar); $code_real = $rchar.$hash_real; $code_fake = $rchar.$hash_fake; $_SESSION["hashcode"] = $hash_real; // oppure usiamo i cookie con: setcookie('hashcode',$hash_real, 0, '/'); ?>
Utilizziamo jQuery nell’head della pagina:
1
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
Aggiungiamo, al codice già visto, 2 nuovi funzioni:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<script type="text/javascript"> <!-- $(document).ready(function() { $('#<?php echo $code_real; ?>').attr("autocomplete","off"); $('.<?php echo $code_real; ?>').css('display','none'); $('#formtime').append('<input type="hidden" id="rchar" name="rchar" value="<?php echo $rchar; ?>" />'); $('#subject').bind('copy paste', function(e) { e.preventDefault(); }); $('#subject').keypress(function() { if ($('#<?php echo $rchar; ?>_control').length == 0) { $('#subject').append('<input type="hidden" id="<?php echo $rchar; ?>_control" name="<?php echo $rchar; ?>_control" />'); $('#<?php echo $rchar; ?>_control').val('<?php echo $code_real; ?>'); } }); }); //--> </script>
Vediamo cosa fanno nel dettaglio le 2 funzioni:
1 2 3 4 5 6 7 8 9 10
$('#subject').bind('copy paste', function(e) { e.preventDefault(); }); $('#subject').keypress(function() { if ($('#<?php echo $rchar; ?>_control').length == 0) { $('#subject').append('<input type="hidden" id="<?php echo $rchar; ?>_control" name="<?php echo $rchar; ?>_control" />'); $('#<?php echo $rchar; ?>_control').val('<?php echo $code_real; ?>'); } });
Sono agganciate al campo “subject” ma possiamo scegliere il campo che preferiamo (ottimo il campo “email” se presente).
Prima di tutto impediamo il “copia/incolla” su quel campo perchè, se venisse usata la funzione “incolla” con il tasto destro del mouse, non verrebbe intercettato l’evento “keypress”.
Il modulo html, invece, non subisce cambiamenti rispetto agli esempi precedenti.
Una volta intercettato l’evento “keypress” (valido anche su tastiere virtuali di smartphone e tablet) viene inserito un campo che ha, come nome, il carattere di controllo più un suffisso e, come valore, il codice hash generato in precedenza.
L’ulteriore controllo, infatti, si basa proprio sul fatto che i tool automatici non scatenano l’evento keypress e questo ci permette di inserire un ulteriore “checkpoint” sulla presenza o meno del campo “control”.
Vediamo la pagina di invio:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
<?php /* * Filename: result4.php */ session_start(); $salt = 'parola-segreta'; $minsec = 1; $now = time(); // il metodo di invio è post if ($_SERVER['REQUEST_METHOD'] == 'POST') { // esiste il campo con il carattere random e non è vuoto if (!empty($_POST['rchar'])) { // esiste il campo con la data e non è vuoto if (!empty($_POST['formtime'])) { // il campo con la data è numerico if (is_numeric($_POST['formtime'])) { // la data di invio è futura rispetto a quella del form if ($now > $_POST['formtime']) { // il tempo trascorso tra il caricamento e l'invio è maggiore di n secondi if (($now - $_POST['formtime']) > $minsec) { // esiste la sessione if (isset($_SESSION['hashcode'])) { // se il contenuto della sessione è uguale alla chiave generata con il salt if ($_SESSION['hashcode'] == md5($salt.$_POST['formtime'].$_POST['rchar'])) { // ho i dati per comporre il nome del campo honeypot $honeypot = $_POST['rchar'].$_SESSION['hashcode']; // ho i dati per comporre il campo control $idcontrol = $_POST['rchar']."_control"; // controllo l'esattezza del campo control if (!empty($_POST[$idcontrol]) && $_POST[$idcontrol] == $honeypot) { // il campo fake esiste ed è vuoto if (isset($_POST[$honeypot]) && empty($_POST[$honeypot])) { $result = "messaggio scritto da umani: $_POST[$idcontrol]"; } else { $result = "bot: riempito campo fake o mancante"; } } else { $result = "bot: non esiste il campo control"; } } else { echo "codice sessione: " .$_SESSION['hashcode']; echo "<br />"; echo "codice generato: " .md5($salt.$_POST['formtime'].$_POST['rchar']); echo "<br /><br />"; $result = "bot: il contenuto della sessione non corrisponde al codice generato"; } } else { $result = "bot: non esiste la sessione"; } } else { $result = "bot: troppo veloce - tempo di invio: " .($now - $_POST['formtime']). " secondi"; } } else { $result = "bot: troppo veloce - tempo di invio: futuro"; } } else { $result = "bot: time manomesso"; } } else { $result = "bot: manca timestamp"; } } else { $result = "bot: manca il campo rchar"; } } else { $result = "bot: la request è diversa da POST"; } /**************** INVIO EMAIL ******************/ $dest = "miaemail@provider.com"; $headers = "X-Mailer: PHP ".phpversion()."\n"; $headers .= "From: $_POST[email] <$_POST[email]>\n"; $headers .= "Return-Path: $dest <$dest>\n"; $headers .= "Reply-To: $dest <$dest>\n"; $headers .= "Date: ".date("H:i:s")." ".date("d/m/Y")."\n"; $headers .= "Delivered-to: $dest <$dest>\n"; $headers .= "MIME-Version: 1.0\n"; $corpo = "ora invio: " .$now. "\n ora form: " .$_POST['formtime']. "\r\n" .stripslashes($_POST["message"]); if (isset($honeypot) && !empty($_POST[$honeypot])) { $corpo .= "\r\n honeypot:" .stripslashes($_POST[$honeypot]); } //invio dell'email mail($dest,$result,$corpo,$headers); setcookie('hashcode','', -1000, '/'); echo $result; ?>
Ricostruiamo il nome del campo “control” mettendo insieme il carattere random e il suffisso “_control”. Una volta fatto questo siamo in grado di confrontare il valore del campo con il nostro codice hash. Se tutto coincide il form è valido e possiamo andare avanti con la validazione.
1 2 3 4 5 6 7 8
// ho i dati per comporre il campo control $idcontrol = $_POST['rchar']."_control"; // controllo l'esattezza del campo control if (!empty($_POST[$idcontrol]) && $_POST[$idcontrol] == $honeypot) { // vado avandi con la validazione }
Download esempi
Per scaricare gli esempi potete cliccare su antispam-form.zip.
Attendo eventuali commenti da chi ha deciso di provare.
bell’articolo! complimenti!!!
l’ho provato e funziona bene come tecnica