domenica 2 marzo 2008

PHP: Proteggere un form da spam con codice captcha

Un form di contatti è un modo semplice per ricevere messaggi o altre informazioni dai visitatori del proprio sito.

Questo tipo di form può però diventare un facile bersaglio per programmi (spambot) creati per inviare messaggi indesiderati in maniera completamente automatica anche a centinaia di form contemporaneamente (o quasi).

Un modo per proteggersi è l'utilizzo di un codice di verifica captcha. Anche se credo che tutti sappiano di che si tratta visto che sono usati dovunque, due parole si possono spendere. Innanzi tutto captcha è un acronimo che sta per "Completely Automated Public Turing test to tell Computers and Humans Apart". Lasciando da parte la spiegazione su cosa sia il test di Turing (per chi non lo sa è ed è interessato a saperlo hanno inventato apposta i motori di ricerca) si può tradurre più o meno liberamente: test pubblico e completamente automatico per distinguere gli esseri umani dai computer.

La forma di test captcha più comunemente usata in un form consiste nella visualizzazione di una sequenza di caratteri ruotati o distorti e su uno sfondo non uniforme. L'esatta sequenza deve essere riportata in un campo apposito del form, altrimenti l'invio dei dati viene rifiutato. Il riconoscimento di caratteri in queste condizioni è relativamente facile per un essere umano mentre scrivere un programma in grado di fare la stessa cosa è molto più complesso anche se non impossibile, come dimostra il fatto che molti tipi di captcha sono state decifrate con software appositi.

Comunque anche se non è un metodo del tutto infallibile l'uso di un test captcha può bloccare la gran parte dei programmi spambot riducendo drasticamente la quantità di spam ricevuta attraverso un form di contatti.

Classe php per generare immagini captcha

Scrivere uno script php che generi immagini adatte ad un test captcha non è proprio una cosa banale, però esistono classi già pronte liberamente utilizzabili. L'esempio che vedremo è basato su PHP Captcha ( http://www.ejeliot.com/pages/2 ).

Il sito è in inglese per cui ho pensato tornasse utile a molti una breve spiegazione con sorgente di esempio.

Per prima cosa suggerisco di scaricare il pacchetto dal sito (PHP-Captcha), scompattarlo in una directory del vostro sito o web server locale ed aprire index.php nel vostro browser per eseguire il semplice script dimostrativo.

La libreria richiede che sul server siano installate le librerie grafiche GD con il supporto per i font TrueType. Se quando lanciate lo script di esempio non vedete l'immagine, la mancanza delle librerie grafiche è la causa più probabile del problema. Dovrebbe comunque essere una situazione abbastanza rara in quanto quasi tutti gli host le includono.

Il contenuto del pacchetto è il seguente

  • php-captcha.inc.php: libreria PHP Captcha
  • captcha.php script per la generazione dell'immagine captcha.
  • index.php esempio di form con codice di validazione.
  • fonts/ cartella con i fonts TrueType usati per la generazione dell'immagine.

File captcha.php

<?php
  require('php-captcha.inc.php');

Include il file con la definizione della classe.

$fonts = array('fonts/VeraBd.ttf', 'fonts/VeraIt.ttf',
'fonts/Vera.ttf');
$captcha = new PhpCaptcha($fonts, 120, 60);

Il costruttore riceve tre argomenti

  • array con i percorsi dei font da utilizzare per la generazione dei caratteri
  • larghezza dell'immagine in pixel
  • altezza dell'immagine in pixel
$captcha->setNumChars(4);

Specifichiamo che la captcha sia formata da 4 caratteri casuali. Se impostate un codice più lungo ricordatevi di aggiustare la larghezza dell'immagine, perché questo non avviene automaticamente. Altrimenti rischiate che uno o più caratteri siano 'tagliati' e di conseguenza risulti sempre impossibile inserire il codice corretto.

$captcha->SetNumLines(80);

Impostiamo il numero delle righe di disturbo che vengono visualizzate sullo sfondo del codice. Se si omette questa riga il valore predefinito è 70.

$captcha->UseColour(true);

Sia le lettere che le linee di disturbo sono a colori. Se si omette questa riga l'immagine sarà generata in toni di grigio.

//$captcha->DisplayShadow(true);

Visualizza un'ombreggiatura per i caratteri del codice. Questa opzione è commentata nel sorgente perché a mio parere rende i caratteri troppo difficili da leggere. Fate qualche prova con e senza questa opzione e decidete se essere o meno d'accordo con me.

$captcha->SetMinFontSize(13);
$captcha->SetMaxFontSize(19);

Impostiamo la dimensione minima e massima del font per i caratteri del codice. Anche in questo caso bisogna fare attenzione a non specificare una dimensione font troppo grande per la misura dell'immagine. Se si omettono queste righe i valori predefiniti sono rispettivamente 16 e 25.

$captcha->SetCharSet('A-Z,1-9');

Specifichiamo quali caratteri devono essere utilizzati per il codice. Nell'esempio il codice potrà essere formato da caratteri alfabetici maiuscoli e numeri da 1 a 9 (perché lo zero si confonde facilmente con la lettera O). Si possono specificare più sequenze di caratteri separate da virgola. Potreste voler sperimentare con altre impostazioni come ad esempio

  • $captcha->SetCharSet('A-Z,a-z') Tutti i caratteri alfabetici maiuscoli e minuscoli
  • $captcha->SetCharSet('0-9') Solo numeri

Se si omette questa opzione il valore predefinito è A-Z: il codice sarà formato solo da caratteri maiuscoli, niente numeri.

$captcha->Create();
?>

A questo punto l'immagine captcha può essere generata.

Ci sono alcune altre opzioni di configurazione, ma quelle viste sono più che sufficienti per personalizzarvi la captcha come desiderate.

Ne cito solo un'altra non utilizzata nell'esempio, ma che può tornare utile.

$captcha->SetBackgroundImages(array_immagini);

Si passa come argomento un array con i percorsi di diversi file immagine. Una di queste immagini sarà scelta a caso ed usata come sfondo per il codice captcha al posto delle righe casuali.

File index.php

In gran parte contiene codice html per la creazione del form per cui non lo commento tutto. Richiamo la vostra attenzione su un paio di punti importanti.

<?php require('php-captcha.inc.php');?>

La libreria deve essere inclusa in testa al file prima di ogni tag html e ogni altro output. Fate anche attenzione a non lasciare righe vuote prima del tag di apertura php altrimenti avrete un errore.

if($_SERVER['REQUEST_METHOD'] == 'POST') {
 if (!PhpCaptcha::Validate($_POST['captcha'])) {
   echo 'Codice captcha errato!<hr />';
 } else {
   ...
 }
}

Questa è la parte che controlla che il codice inserito corrisponda a quello visualizzato nell'immagine. Non c'è molto da dire perché fa tutto la classe.

<img style="border:1px solid" src="captcha.php" alt="captcha" /><br />
<input name="captcha" type="text" size="6" /><br />

Visualizza l'immagine captcha e il campo per l'inserimento del codice. Lo script che genera dinamicamente l'immagine (captcha.php, esaminato in dettaglio sopra) deve essere inserito come valore dell'attributo src del tag <img>.

È tutto.

Va detto che utilizzare un test captcha per proteggere un form è una decisione da prendere con attenzione. Non è una soluzione infallibile a tutti i problemi di spam e può creare problemi di accessibilità al vostro sito. Se decidete di seguire questa strada spero che il codice proposto (PHP-Captcha) possa esservi utile.

6 commenti:

Fabrizio ha detto...

Ciao,
La tua guida al captcha è molto utile, ma non ho capito in quale punto impostare l'indirizzo e-mail a cui inviare il risultato della compilazione del form...

Massimo ha detto...

Perché quella parte non c'è :)
Il sorgente è solo per dimostrare l'uso della libreria per la captcha, non voleva essere uno script completo. La parte per l'invio dell'email va aggiunta, magari prendendo spunto da qualche form2email script che trovi facilmente in rete.

Fabrizio ha detto...

Scusa ancora per il disturbo,
ma lo script lo devo mettere al posto della parte relativa al piccolo form di esempio?
Se è troppo lungo da spiegare ignora pure questo messaggio...
Grazie per la disponibilità!

Massimo ha detto...

Le istruzioni per l'invio dell'email devono essere messe al posto delle istruzioni echo che nel codice di esempio visualizzano i dati inseriti. Magari posterò un esempio più completo in uno dei prossimi articoli.

Anonimo ha detto...

scusa ma il codice con SERVER['REQUEST_METHOD
va messo dopo body?
a me non funziona qualsiasi cosa digito lo pubblica lo stesso!

Massimo ha detto...

Sì quella parte va dopo BODY. Se non ti funziona il problema deve essere un altro, forse la gestione delle sessioni.

Posta un commento

Nota. Solo i membri di questo blog possono postare un commento.