Salve a tutti.
A prescindere da tutti i discorsi circa l'accessibiltà e l' usabilità delle immagini di CAPTCHA (spesso illeggibili per persone senza alcun problema, figuriamoci per coloro che hanno handicap), penso che tutti noi abbiamo avuto problemi di spam nei nostri siti.
Più volte ho letto dei thread di persone che avevano necessità di inserire un sistema di captcha nelle loro form per cercare di far diminuire i messaggi di spam dalle loro pagine web, e molte di quelle volte la risposta che si leggeva suonava tipo “mi spiace, ma non hai la possibilità di creare immagini da codice perché il tuo spazio web non supporta le librerie GD”.
Mi ci sono messo di punta ed ho pensato che, sfruttando le possibilità che un programma come Adobe Flash mette a disposizione, si poteva comunque creare un'immagine con del testo creato al volo da utilizzare come CAPTCHA pure in assenza delle tanto blasonate librerie GD.
Per farlo, è bastato creare due file PHP (uno chiamato index.php che è quello che metteremo a disposizione dei nostri utenti, l'altro sessione.php che verrà utilizzato dal nostro filmato di Flash), ed il nostro filmato Flash chiamato captcha.swf, a sua volta generato dal sorgente chiamato captcha.fla. Nella cartella in cui sono presenti questi file, è stata poi creata una cartella /contenitore per salvare i nostri file di sessione.
Cominciamo ad illustrare captcha.fla, partendo dal presupposto che io l'ho creato con una versione di Flash piuttosto datata (Flash MX 2004) ed usando ActionScript 2.0.
Aprite un nuovo progetto, impostate le dimensioni che preferite, e create due livelli. Il primo (quello inferiore) chiamato background, l'altro captcha.
Nel primo livello va importata un'immagine di sfondo (meglio se PNG) con il classico “rumore” per rendere il tutto un po' più illeggibile, ma potete anche creare un movieclip con una animazione. Questa parte è letteralmente a vostro gusto. Ricordate che spesso, però, per rendere troppo difficile la vita agli spammer ed ai loro OCR, si rende difficile la vita anche ai nostri poveri utenti!
Nel secondo livello, invece, dovete creare una etichetta di testo dinamico e chiamarla testo_txt. A questa etichetta associate le dimensioni ed il carattere che preferite, ma ricordate di usare delle font comuni oppure di importare nel file anche il font che avete scelto... a scapito però delle dimensioni del file SWF.
Sempre nello stesso livello, inserite il seguente codice:
codice:
var lv:LoadVars = new LoadVars();
lv.onLoad = function(success:Boolean) {
if (success) {
var captcha:String = this.str_captcha;
var num:Number = captcha.length;
for (i = 0; i < num; i++){
var singola_lettera:String = captcha.substr(i, 1);
trace(singola_lettera);
testo_txt.text += singola_lettera;
testo_txt.selectable = false;
}
} else {
testo_txt.text = "Errore!!!";
}
};
lv.load("sessione.php"); // commentare in caso di debug
// lv.load("sessione.txt"); // DE-commentare in caso di debug
Spiego brevemente.
Si crea un nuovo oggetto LoadVars (lv).
Al momento che questo oggetto viene istanziato, il programma legge il file sessione.php presente nella nostra root. In caso di successo di questa operazione, il codice va a leggere il contenuto del file in questione, che sarà così formattato:
codice:
str_captcha=xxxxxx
Il ciclo di FOR presente nel codice, conta di quante lettere è composta la stringa ed effettua un ciclo per estrarne una per volta e mandarla in stampa nel campo di testo dinamico creato precedentemente.
L'opzione testo_txt.selectable = false; l'ho aggiunta semplicemente per evitare il copia/incolla del testo nel campo input della form.
Ho appositamente lasciato il trace() nel codice per la fase di debug, che vi consiglio di fare con un file di testo da richiamare al posto di quello PHP (sessione.txt) nel quale avrete avuto premura di scrivere il seguente semplice testo:
codice:
str_captcha=abcdef123
Una volta che avrete testato il funzionamento del filmato, non vi resta che esportare in formato SWF, avendo premura di scegliere le migliori impostazioni di compressione, nel caso abbiate usato una immagine per sfondo, per far risultare il filmato stesso di dimensioni ridotte.
Spero che per la parte Flash di questo progetto sia tutto chiaro.... passiamo quindi alla questione PHP.
Partiamo dal file che legge la sessione per Flash, la quale deve fare poche azioni: leggere la variabile di sessione e mandarla in echo in un formato che sia leggibile per flash.
Ecco il codice di sessione.php:
Codice PHP:
<?php
/*
###############################
## CAPTCHA in PHP/HLASH ##
## ======================= ##
## by © Alcio ##
## per tutti voi del forum ##
###############################
*/
ini_set("session.save_path","contenitore");
session_start();
$hash = $_SESSION['stringa'];
echo "str_captcha=$hash";
?>
A parte il “momento di gloria” che mi sono auto-regalato nel commento iniziale, il codice è piuttosto semplice: si imposta con ini_set(), la cartella delle sessioni diversa da quella di default del server. È solo un piccolo aiutino alla sicurezza globale dello script, ma - unitamente ad altri accorgimenti - piuttosto utile.
Si avvia l'utilizzo delle sessioni per lo script, e si legge il contenuto di quella attiva, associando la variabile $_SESSION['stringa'] ivi contenuta alla nuova $hash.
Il file compone quindi la stringa formattata per Flash con la conclusiva echo.
Tutto qui. Semplice, no?
Passiamo ora al cuore della nostra applicazione, il file index.php.
Codice PHP:
<?php
/*
###############################
## CAPTCHA in PHP/HLASH ##
## ======================= ##
## by © Alcio ##
## per tutti voi del forum ##
###############################
*/
ini_set("session.save_path","contenitore");
session_start();
// controllo sull'input
function validate_texts($str){
$str = trim($str);
return (bool) preg_match("^[[:alnum:]]+$^", $str);
}
$clicked = $_POST['click'];
if (!$clicked) {
// imposto la variabile nella sessione
$stringa = SHA1(microtime());
$hash = str_shuffle($stringa);
$hash = substr($hash, 0, 8);
$_SESSION['stringa'] = $hash;
// stampo la form
echo "
<h1>Controllo CAPTCHA</h1>
<hr />
<form action=\"index.php\" method=\"POST\" enctype=\"multipart/form-data\">
<object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0\" width=\"200\" height=\"70\" id=\"captcha\" align=\"middle\">
<param name=\"allowScriptAccess\" value=\"sameDomain\" />
<param name=\"movie\" value=\"captcha.swf\" />
<param name=\"quality\" value=\"high\" />
<param name=\"bgcolor\" value=\"#e0dfe3\" />
<embed src=\"captcha.swf\" quality=\"high\" bgcolor=\"#e0dfe3\" width=\"200\" height=\"70\" name=\"captcha\" align=\"middle\" allowScriptAccess=\"sameDomain\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.macromedia.com/go/getflashplayer\" />
</object>
<label for=\"tascrivi\">Trascrivi la stringa:</label>
<input type=\"password\" name=\"stringa\" id=\"stringa\" />
</p>
<input type=\"submit\" value=\"Invia\" name=\"click\" id=\"click\" />
</form>
";
} else {
$stringa_da_controllare = $_POST['stringa'];
$stringa_nella_sessione = $_SESSION['stringa'];
if(validate_texts($stringa_da_controllare)) {
if ($stringa_da_controllare === $stringa_nella_sessione) {
echo "<h2>Controllo superato</h2>";
} else {
echo "<h2>Controllo NON superato</h2>";
}
} else {
echo "Spiacente ma alcuni caratteri presenti nella stringa non sono validi";
}
session_unset();
session_destroy();
}
?>
Anche in questo caso, sempre dopo il messaggio auto-celebrativo, la prima cosa che si deve fare è impostare la directory di default dove lo script andrà a salvare/leggere le sessioni, quindi dare lo start alle sessioni stesse.
La funzione immediatamente successiva, non è altro che un controllo sull'input: si occuperà di ritornare FALSE nel caso la stringa passata dall'utente presenti caratteri diversi da quelli alfanumerici.
Si recupera poi la variabile associata al pulsante submit della form: se non istanziata significa che l'utente non ha premuto il pulsante, per cui stampo la form stesa in attesa dell'interazione.
In questa fase, è anche necessario creare una stringa alfanumerica casuale: la nostra CAPTCHA.
In questo esempio viene creata semplicemente crittando in SHA1 il microtime() del momento in cui lo script viene eseguito. Le lettere vengono mescolate (str_shuffle()), quindi ne vengono estratte solo una certa quantità con substr().
La nuova stringa così creata viene quindi salvata nella sessione.
La form successiva è classica, se non per il richiamo al file captcha.swf, il quale stamperà a video la variabile di sessione appena creata sotto forma di filmato flash.
Il resto dello script credo sia piuttosto semplice da capire.
La stringa riportata dall'utente viene filtrata con la funzione di cui sopra, e confrontata con la variabile salvata nella sessione.
Se il confronto da' esito positivo, lo script comunica l'avvenuto riconoscimento, altrimenti comunica il contrario.
Spero di essere stato chiaro nell'esposizione, e spero di ricevere commenti sia sul PHP sia sul Flash, in modo da migliorarlo, se ce ne fosse bisogno.
Per quello che concerne la sicurezza, ho evitato di appesantire troppo questo post ed i suoi script, riducendo davvero all'osso i controlli.
Per quello che riguarda il file di flash, invece, si potrebbe pensare di modificare ogni singola lettera con una inclinazione, degli effetti di blur o quant'altro.
Aspetto i vostri commenti, e sarebbe davvero bello vedere questo thread trasformato in una pillola!