Proteggere mediante password un file a cui si accede da un link con javascript, jQuery e php

back to HomePage

 

 

 

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!