Proteggere mediante password un file
a cui si accede da un link con javascript, jQuery e php
Supponiamo che sul vostro sito abbiate
un link ad una pagina html di cui volete restringere l'accesso alle sole
persone dotate di password.
Il problema non è banale: la prima
cosa che potrebbe venirvi in mente è di scrivere una routine javascript esterna
richiamata mediante una istruzione del tipo:
<script type="text/javascript"
src="MyScript.js"></script>
Nel file MyScript.js andrebbe definita
una funzione CheckPassword del tipo seguente:
function CheckPassword(MyDocument,MyForm)
{
var
Password = prompt("DIGITA LA PASSWORD","");
if (Password=="abracadabra")
{
MyForm.href='MyPage.htm';
return true;
}
else
{
MyForm.href='';return false;}
}
}
Nel documento html andrebbe inserito
un tag <a> il cui link si attiva solo se la funzione CheckPassword, richiamata con
il click del mouse, restituisce valore "true":
<a target =
"_parent" href="" onclick="return ApriPagina(window.document,this);">
La pioggia nel pineto
</a>
Tuttavia questo sistema ha una pecca
fatale: il file MyScript deve contenere l'indicazione della password e l'URL
del file, e un hacker, visualizzato il codice html, andrà ad aprire
Nondimeno, alcuni dei meccanismi di
questo sistema saranno utilizzati anche nella soluzione che stiamo per
proporre:
● Il passaggio, come parametri, della finestra e
del tag (rispettivamente "window.document" e "this"), serve
per poter gestire password multiple, relative a documenti diversi ovvero a link
diversi nello stesso documento. Per maggiore semplicità, in questa sede tali
possibilità non sono implementate.
● Il meccanismo di protezione si basa sul fatto
che, se la routine restituisce valore "false" l'esecuzione del codice
collegata all'evento "onclick" viene bloccata dal browser.
● l'URL del documento non compare nell'attributo
"href:" del tag <a>, in modo che, se anche si riesce a forzare
l'attivazione del link questo non conduce da nessuna parte
Occorre chiaramente pensare a
soluzioni alternative. Nascondere la password in un tag con l'attributo
"hidden" o con l'attributo CSS "display : none" oppure
"visible : none"? Neanche a pensarci: questi tag sono perfettamente
visibili nel sorgente della pagina.
In qualsiasi file mettiate una
password non criptata, dovrete inserire l'URL nel codice di controllo e
l'hacker andrà a leggerlo.
Una soluzione può essere la
criptazione. Crittare e decrittare una stringa con le funzionalità jQuery è
facile, ma il problema da affrontare è: con cosa confronto la stringa
decrittata?
Se si criptasse l'intero file, il
problema non sussisterebbe, ma questa è una soluzione poco comoda: saremmo
costretti a decrittare e crittare di nuovo il file ad ogni minima modifica.
La soluzione che si è adottata qui è quella
implementata nella pagina PasswordPage.htm (cliccate per visualizzarla).
Questa pagina presenta due parti, che
andrebbero messe in due pagine separate. Il primo tag è:
<p style="font-weight:bold;"
align="center">
Questo
è il link protetto da password:</br></br>
<a
id="MyAnchor"
style="font-weight:bold;" target="_parent"
href=""
onclick="document.getElementById('DisplayResult').innerHTML='';return
VerificaPassword();">MyLink</a>
</p>
Cliccando sul link viene attivata la
funzione VerificaPassword()inserita in uno script javascript:
function
VerificaPassword()
{
document.getElementById("DisplayResult").innerHTML="";
Password=prompt("Digita la
password");
if(Password.length!=4){alert("La
password deve essere esattamente di 4 caratteri!");return false;}
$.ajax
({
type: "GET",
url:
"EncryptDecrypt.php?stringa="+Password+"&action=decrypt",
async: false,
dataType: "text",
success: function(result)
{
if(result=="failure"){return
false;}
else
{
document.getElementById("MyAnchor").attributes["href"].value=result;
return true;
}
}
});
}
Questa funzione richiama il codice
php contenuto nel file EncryptDecrypt.php col metodo GET, passandogli come parametro
"action=decrypt". Ecco il codice php:
<?php
$action = (string)$_GET['action'];
if
($action=="encrypt"){CambiaPassword();}
if
($action=="decrypt"){VerificaPassword();}
function CambiaPassword()
{
$key =
substr((string)$_GET['stringa'],0,4);
$string = (string)$_GET['stringa'];
$encrypted_string =
mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_CBC);
$decrypted_string =
mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted_string, MCRYPT_MODE_CBC);
$output="";
for($index=0;$index<strlen($encrypted_string);$index++)
{
$output=$output.ord(substr($encrypted_string,$index,1)).";";
}
$retrieved_encrypted_string="";
for($index=0;$index<strlen($output);$index++)
{
$carattere="";
$substring=substr($output,$index);
$puntatore=$index;
if(substr($output,$puntatore,1)==";"){$puntatore++;}
while(substr($output,$puntatore,1)!=";"){$carattere=$carattere.substr($output,$puntatore,1);$puntatore++;}
$retrieved_encrypted_string=$retrieved_encrypted_string.chr($carattere);
$index=$puntatore;
}
$retrieved_decrypted_string=mcrypt_decrypt(MCRYPT_RIJNDAEL_256,
$key, $retrieved_encrypted_string, MCRYPT_MODE_CBC);
$handle =
fopen("Password.txt","w");
$result = fwrite($handle,$output);
fclose($handle);
$handle =
fopen("Password.txt","r");
$reading =
fread($handle,filesize("Password.txt"));
fclose($handle);
echo("action : encrypt</br>");
echo("Stringa scritta nel file
Password.txt : \"" . $output . "\"</br>");
echo("Questa
stringa e' la criptazione della stringa :
\"".$decrypted_string."\"</br>");
echo("mediante la chiave :
\"".substr($retrieved_decrypted_string,0,4)."\"</br>");
echo("(la chiave di criptazione viene
aggiunta all'inizio della stringa da criptare: in tal modo la decrittazione
fornisce contemporaneamente</br>la stringa da confrontare con la password
digitata e il nome del file da aprire)");
}
function VerificaPassword()
{
$key =
(string)$_GET['stringa'];
$handle =
fopen("Password.txt","r");
$reading =
fread($handle,filesize("Password.txt"));
fclose($handle);
$retrieved_encrypted_string="";
for($index=0;$index<strlen($reading);$index++)
{
$carattere="";
$substring=substr($reading,$index);
$puntatore=$index;
if(substr($reading,$puntatore,1)==";"){$puntatore++;}
while(substr($reading,$puntatore,1)!=";"){$carattere=$carattere.substr($reading,$puntatore,1);$puntatore++;}
$retrieved_encrypted_string=$retrieved_encrypted_string.chr($carattere);
$index=$puntatore;
}
$retrieved_decrypted_string=mcrypt_decrypt(MCRYPT_RIJNDAEL_256,
$key, $retrieved_encrypted_string, MCRYPT_MODE_CBC);
if(substr(trim($retrieved_decrypted_string),0,4)==trim($key))
{
echo(substr(trim($retrieved_decrypted_string),4));
}
else
{
echo("failure");
}
}
?>
Il codice contiene due funzioni: CambiaPassword() e VerificaPassword(),
che sono attivate rispettivamente se "action=encrypt" e
"action=decrypt".
Osserviamo il codice di VerificaPassword():
function VerificaPassword()
{
$key =
(string)$_GET['stringa'];
$handle =
fopen("Password.txt","r");
$reading =
fread($handle,filesize("Password.txt"));
fclose($handle);
$retrieved_encrypted_string="";
for($index=0;$index<strlen($reading);$index++)
{
$carattere="";
$substring=substr($reading,$index);
$puntatore=$index;
if(substr($reading,$puntatore,1)==";"){$puntatore++;}
while(substr($reading,$puntatore,1)!=";"){$carattere=$carattere.substr($reading,$puntatore,1);$puntatore++;}
$retrieved_encrypted_string=$retrieved_encrypted_string.chr($carattere);
$index=$puntatore;
}
$retrieved_decrypted_string=mcrypt_decrypt(MCRYPT_RIJNDAEL_256,
$key, $retrieved_encrypted_string, MCRYPT_MODE_CBC);
if(substr(trim($retrieved_decrypted_string),0,4)==trim($key))
{
echo(substr(trim($retrieved_decrypted_string),4));
}
else
{
echo("failure");
}
}
Dopo aver acquisito il valore della
stringa da criptare, contenuto nel parametro "stringa=…" viene aperto
il file Password.txt e ne viene letta la stringa. La stringa in Password.txt, come detto, è la
criptazione della stringa composta dalla password corretta seguita dal nome del
file da proteggere. Nel nostro esempio abbiamo criptato la stringa:
"darkpoesia.htm"
tramite la chiave di criptazione
fornita dalla stessa password:
"dark"
Se l'utente fornisce la giusta
password ("dark
") la decrittazione restituisce di nuovo la stringa:
"darkpoesia.htm"
e i primi 4 caratteri della stringa
decrittata vengono confrontati con la password fornita dall'utente.
Se il riscontro è positivo viene
inviata alla routine javascript chiamante (quella della funzione VerificaPassword()
della pagina PasswordPage.htm) la parte di stringa contenente l'URL:
echo(substr(trim($retrieved_decrypted_string),4));
altrimenti viene inviata la stringa
"failure":
echo("failure");
Nel primo caso VerificaPassword()
provvede a scrivere l'URL nell'attributo "href" del tag <a> e a
restituire valore "true":
document.getElementById("MyAnchor").attributes["href"].value=result;
return true;
Nel secondo caso VerificaPassword()restituisce
valore "false" e il link non si attiva (del resto, non potrebbe,
essendo il campo "href" vuoto):
if(result=="failure"){return
false;}
Ora passiamo ad illustrare la
restante parte della pagina PasswordPage.htm, che, lo ripetiamo, andrebbe ospitata in una pagina separata,
in quando è la parte che consente di specificare l'URL del file da proteggere e
la password. Questo file non andrebbe
lasciato sul server, ma andrebbe rimosso subito dopo la sua utilizzazione:
l'unica cosa che deve rimanere è la stringa di controllo nel file Password.txt,
che viene generata premendo il bottone "INVIO".
In sede di creazione della password
viene anzitutto richiesta la URL del file da proteggere:
<p style="font-weight:bold;"
align="center">
Scrivi il nome del file
da proteggere con la password:</br></br>
<input id="FileName" type="text"
style="text-align:center;font-family:'Times New Roman;"
value="poesia.htm"
onfocus="document.getElementById('DisplayResult').innerHTML='';"/>
</p>
Noi abbiamo già inserito il riferimento al file poesia.htm che contiene una
poesia di Gabriele D'Annunzio.
Va quindi inserita la password:
<p style="font-weight:bold;"
align="center">
Cambia la password (4
caratteri alfanumerici):</br></br>
<input
style="text-align:center;font-family:'Times New
Roman;font-size:100%;" id="StringToEncrypt"
type="text" value="dark"
onfocus="document.getElementById('DisplayResult').innerHTML='';"/></br></br>
<input type="button"
style="font-family:'Times New Roman'" value="INVIO"
onclick="CreaPassword();"/>
</p>
Noi abbiamo già inserito la password
corrente ("dark"), che l'utente potrà cambiare.
Prima di cambiare la
password, provate a cliccare sul link MyLink e a fornire la
password "dark": vedrete che aprirà il file poesia.htm. Provate a
fornire una password diversa e vedrete che il file non si aprirà.
Prima di cambiare la
password, provate a cliccare sul bottone INVIO, senza modificare i valori preimpostati nei campi testo, per vedere come lavora la funzione CambiaPassword() del file EncryptDecrypt.php. La funzione genererà un report del processo di criptazione che
vi consiglio di leggere.
A questo punto, potrete divertirvi a
cambiare la password e poi a cliccare sul link per verificare come ora essa sia
cambiata.
Notate che questa sezione
della pagina vi consente di impostare una password per qualsiasi file di cui voi scriverete l'URL nell'apposito campo.
Se immetterete nella vostra pagina un link con il codice sopra indicato e la
routine javascript con la funzione VerificaPassword()
otterrete la protezione per tale file e non per il file poesia.htm.
Come già detto, password e file sono
criptati insieme, come unica stringa, utilizzando come chiave di criptazione la
password.
Quando viene premuto il bottone INVIO
viene richiamato il file EncryptDecrypt.php e vengono passati con il metodo GET due parametri: "action=crypt"
e "stringa=…"
Il primo attiverà la funzione CambiaPassword() e le fornità la
stringa da crittografare e inserire nel file Password.txt
Ecco il codice di CambiaPassword():
function CambiaPassword()
{
$key =
substr((string)$_GET['stringa'],0,4);
$string = (string)$_GET['stringa'];
$encrypted_string = mcrypt_encrypt(MCRYPT_RIJNDAEL_256,
$key, $string, MCRYPT_MODE_CBC);
$output="";
for($index=0;$index<strlen($encrypted_string);$index++)
{
$output=$output.ord(substr($encrypted_string,$index,1)).";";
}
$handle
= fopen("Password.txt","w");
$result = fwrite($handle,$output);
fclose($handle);
echo("action
: encrypt</br>");
echo("Stringa scritta nel file
Password.txt : \"" . $output . "\"</br>");
echo("Questa stringa e' la criptazione
della stringa : \"".$string."\" mediante la chiave :
\"".substr($string,0,4)."\"</br>");
echo("(la chiave di criptazione viene
aggiunta all'inizio della stringa da criptare: in tal modo la decrittazione
fornisce contemporaneamente</br>la stringa da confrontare con la password
digitata e il nome del file da aprire)");
}
La prima cosa che fa questo codice è
acquisire i parametri $string e $key, che sono rispettivamente la stringa da
crittografare e la chiave di criptazione
$key = substr((string)$_GET['stringa'],0,4);
$string = (string)$_GET['stringa'];
Il risultato della criptazione viene
inserito nella variabile $encrypted_string:
$encrypted_string = mcrypt_encrypt(MCRYPT_RIJNDAEL_256,
$key, $string, MCRYPT_MODE_CBC);
Questa non è però la stringa
memorizzata nel file Password.txt, perché viene trasformata in una successione
di codici ASCII separati da un punto e virgola dalla seguente routine, che
immette questa stringa trasformata nella variabile $output:
$output="";
for($index=0;$index<strlen($encrypted_string);$index++)
{
$output=$output.ord(substr($encrypted_string,$index,1)).";";
}
Successivamente viene aperto il file
Passworx.txt e vi viene scritta la stringa $output:
$handle =
fopen("Password.txt","w");
$result = fwrite($handle,$output);
fclose($handle);
La stringa memorizzata in
Password.txt ha l'aspetto seguente (si tratta solo di un esempio):
"88;131;147;35;193;167;236;106;116;124;149;239;1;214;144;149;57;58;213;21;70;174;64;74;142;55;103;23;230;31;153;195;"
In questo modo, quando un utente
digita una stringa di login, la routine javascript/jQuery di controllo, dopo
aver letto la stringa criptata tenta di decrittarla con la chiave fornita dall'utente.
Se l'inizio della stringa decrittata corrisponde alla stringa digitata
dall'utente il controllo è positivo, e il codice legge i caratteri successivi
della stringa come URL del file da aprire.
Tutto quello che potrà vedere
l'hacker è una stringa di caratteri contenuti in Password.txt, che per lui non
ha alcun significato. E in tal modo, se non conosce la password, la URL del
documento gli sarà completamente inaccessibile.
Infine, viene inviato un messaggio di
feedback, che compare in fondo alla pagina PasswordPage:
echo("action :
encrypt</br>");
echo("Stringa scritta nel file
Password.txt : \"" . $output . "\"</br>");
echo("Questa stringa e' la criptazione
della stringa : \"".$string."\" mediante la chiave :
\"".substr($string,0,4)."\"</br>");
echo("(la chiave di criptazione viene
aggiunta all'inizio della stringa da criptare: in tal modo la decrittazione
fornisce contemporaneamente</br>la stringa da confrontare con la password
digitata e il nome del file da aprire)");
Per veder funzionare il tutto dovete
fare le seguenti cose:
● Inserite nell'<head> della vostra pagina
il richiamo alla libreria jQuery:
<script
src="jquery-1.11.2.js"></script>
<script
src="jquery-migrate-1.2.1.js"></script>
● Inserite nell'<head> della vostra pagina
la routine javascript/jQuery che contiene la funzione VerificaPassword:
<script language
="JavaScript">
function VerificaPassword()
{
document.getElementById("DisplayResult").innerHTML="";
Password=prompt("Digita la password");
if(Password.length!=4){alert("La password deve essere esattamente
di 4 caratteri!");return false;}
$.ajax
({
type: "GET",
url:
"EncryptDecrypt.php?stringa="+Password+"&action=decrypt",
async: false,
dataType: "text",
success: function(result)
{
if(result=="failure"){return false;}
else
{
document.getElementById("MyAnchor").attributes["href"].value=result;
return true;
}
}
});
}
</script>
● Inserite nella vostra pagina il link al vostro
sito, che richiama la funzione VerificaPassword():
<a id="MyAnchor" style="font-weight:bold;"
target="_parent" href=""
onclick="document.getElementById('DisplayResult').innerHTML='';return
VerificaPassword();">MyLink</a>
● Copiate sul vostro server il file PasswordPage.htm
(cliccate qui per scaricarlo) rinominandolo ImpostaPassword.htm e rimuovete dal
suo codice il tag <a>; in tal modo servirà unicamente come pagina per
cambiare la password
● Create sul vostro server un file di testo
Password.txt (operazione non strettamente necessaria, perché se il codice di
ImpostaPassword.htm non trova la pagina, la crea)
● Copiate sul vostro server il file EncryptDecrypt.php
(cliccate qui per scaricarlo)
Buon lavoro!