Dans cet article, nous allons jouer avec le Zend_Framework et ses filtres.
Plus précisément, Zend_Filter_Encrypt et Zend_Filter_Decrypt.
Nous verrons aussi comment créer ses filtres et chainer les appels.
Le besoin : Créer une url avec une durée de vie définie.
Travaillant pour un société de tirage en ligne, certaines images uploadées par les clients ont une url public.
Pour éviter que les clients hotlink ces images et profite de l’hébergement de la société, on fourni une url à durée de vie limitée de la forme
http://www.webozor.com/image.php?param=SSegseeg7sesegSseofgjsegsegseg6486sef4sefsefijseofseofsef
url et histoire presque fictive bien sur.
Dans la variable param se cache les identifiants de la photo ainsi qu’un timestmap. C’est ce timestamp qui va nous permettre de gerer la durée de vie du lien.
Dans le script image.php on decrypt la chaine pour récupérer les données, dont le timestmap, puis s’il a dépassé son TTL, on affiche une image bidon anti-hotlink a la place de l’image en question.
A chaque affichage de l’image, le param est régénéré avec le timestamp courant. L’url n’est donc valide que pour un certain temps.
Voila pour la théorie, maintenant la pratique.
Pour crypter/decrypter, le ZF fourni 2 filtres, c’est ceux la que nous allons utiliser pour générer l’url.
$_encryption = array( 'key' => '856fhyrdvpkjrfgt', 'algorithm' => MCRYPT_BLOWFISH, 'mode' => MCRYPT_MODE_CBC, 'vector' => '8Rdptk51' ); $dataStart = array( 'chaine1' => 'premiere chaine', 'chaine2' => 'deuxieme chaine', 'time' => time() ); $filter = new Zend_Filter_Encrypt($_encryption); $encodeBin = $filter->filter(Zend_Json::encode($dataStart));
Pour crypter/décrypter, on a besoin de plusieurs paramètres
- key : la clé
- algorithm : l’algo utilisé traiter les données. Appelé aussi cypher
- mode : quel mode sera utilisé par l’algo.
- vector : vecteur d’initialisation a utiliser, appelé souvent IV
il y a en plus un paramètre salt, mais qui sert dans très peu de cas pour utiliser la clé comme grain de sel. Ce paramètre ne sert qu’avec les cipher ayant besoin d’une taille de clé défini, exemple, rijdnael. Pour connaitre la taille
De même vous pouvez récupérer la liste des modes disponibles grâce à la fonction mcrypt_list_modes() et des constantes sont aussi définis pour chacun des modes. Pour comprendre un peu les différents modes, je vous conseille de lire cet article
Vous pouvez récupérer la liste des ciphers disponible grâce à la fonctin mcrypt_list_algorithms() et les constantes pour tout les ciphers qui sont définis dans mcrypt.
A noter que si on ne passe pas de vecteur, il en génère un automatiquement, mais pour pouvoir décrypter, il faut le récupérer avec getVector et le transmettre avec la chaine.
Si on fait un echo de la variable $encodeBin on remarque que le contenu renvoyé est en binaire.
˜7abWëÄùqÌê (r¨ ™‚;ððñÖnf‘æzäëòó¥ô¤Ïõ²*´Ëg�ÓÏì˜g—4o,)}ç볬øÙÔ¸úÊ!ü§vð
il faut donc lui faire une transformation ( un filtre ? ) histoire d’avoir une string alphanumérique à transmettre via l’url. Le plus simple pour ca, c’est de passer par bin2hex() qui va transformer le binaire en hexadécimal.
Ça donnera quelques choses du genre :
981737616257ebc4f971ccea0d2872a82099823b0314f0f0c3b1d66e6691e67ae4ebf2f3a5f4a4cff5b22ab4cb67007fd3cfec160c1198670e97346f2c290c7de7ebb3acf8d9d4b802faca21fca776f0
on aurai put utiliser du base64 pour avoir une chaine un peu plus courte, mais il y a des caractères autres qu’alphanumérique du coup ca peut poser des problèmes d’échappement pendant la transmission.
Une fois la variable reçu, le script image n’a plus qu’a faire le chemin inverse, hexadécimal vers binaire puis passage dans le filtre Decrypt avec les mêmes options que pour l’encrypt bien sur, sinon impossible de retomber sur ses pieds… Comme il n’y a rien de prévu nativement en php pour passer du binaire à l’hexadécimal, nous allons créer une petite fonction.
function hex2bin($hexdata)
{
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2)
{
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
Pour décrypter ensuite
$decodeChaineBin = hex2bin($decodeChaineHexa); $filter = new Zend_Filter_Decrypt($_encryption); $decode = $filter->filter($decodeChaineBin); $dataEnd = Zend_Json::decode(trim($decode));
dans $decodeChaineHexa, on a la string hexa résultat du cryptage.
On crée un filtre Zend_Filter_Decrypt avec les mêmes paramètres que tout a l’heure et on lui transmet le tout.
A noter que pour crypter un array je le transforme en string via les fonctions json, plus courte et lisible que la fonction serialize de php à mon gout.
Au final, on a un fichier de ce genre
$_encryption = array(
'key' => '856fhyrdvpkjrfgt',
'algorithm' => MCRYPT_BLOWFISH,
'mode' => MCRYPT_MODE_CBC,
'vector' => '8Rdptk51'
);
$dataStart = array(
'chaine1' => 'premiere chaine',
'chaine2' => 'deuxieme chaine',
'time' => time()
);
$filter = new Zend_Filter_Encrypt($_encryption);
$encodeBin = $filter->filter(Zend_Json::encode($dataStart));
$encodeHexa = bin2hex($encodeBin);
$decodeChaineHexa = $encodeHexa;
$decodeChaineBin = hex2bin($decodeChaineHexa);
$filter = new Zend_Filter_Decrypt($_encryption);
$decode = $filter->filter($decodeChaineBin);
$dataEnd = Zend_Json::decode(trim($decode));
function hex2bin($hexdata)
{
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2)
{
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
et voila ce que ce qui s’affiche en debug :
Input : Array ( [chaine1] => premiere chaine [chaine2] => deuxieme chaine [time] => 1297373953 ) Binaire : ˜7abWëÄùqÌê (r¨ ™‚;ððñÖnf‘æzäëòó¥ô¤Ïõ²*´Ëg�ÓÏì˜g—4o,)}Þü(?Oj}¥äê<¢4Ѷ Hexa : 981737616257ebc4f971ccea0d2872a82099823b0314f0f0c3b1d66e6691e67ae4ebf2f3a5f4a4cff5b22ab4cb67007fd3cfec160c1198670e97346f2c290c7d0bdefc283f4f6a7da5e4ea3ca234d1b6 Binaire : ˜7abWëÄùqÌê (r¨ ™‚;ððñÖnf‘æzäëòó¥ô¤Ïõ²*´Ëg�ÓÏì˜g—4o,)}Þü(?Oj}¥äê<¢4Ѷ Output : Array ( [chaine1] => premiere chaine [chaine2] => deuxieme chaine [time] => 1297373953 )
on arrive bien à crypter et décrypter la chaine. On peut vérifier que le tableau en Input et Output est identique, apres avoir été subit ces divers filtres…
Dans notre fichier image.php, un test sur la valeur de time permettra de renvoyer l’image ou une image anti hotlink
Le filtre My_Filter_HexToBin
Pour améliorer le script, transformons la fonction hex2bin en filtre.
Je créer un fichier HexToBin.php que je place dans le repertoire My/Filter.
class My_Filter_HexToBin
implements Zend_Filter_Interface
{
public function filter($p_value)
{
$r_value = '';
for ($i = 0; $i < strlen($p_value); $i += 2)
{
$r_value .= chr(hexdec(substr($p_value, $i, 2)));
}
return $r_value;
}
}
Il faut implémenter l’interface Zend_Filter_Interface et renseigner une méthode filter qui va renvoyer la valeur après traitement.
Voila par exemple ce qui pourrai être une partie de la classe Images, charger d’encoder et décoder une string pour la transmettre via l’url.
class Images
{
protected $_encryption = array(
'key' => '84sefSegzo9512',
'algorithm' => MCRYPT_BLOWFISH,
'mode' => MCRYPT_MODE_CBC,
'vector' => '8Rdptk51'
);
public function encode(array $p_data)
{
$filter = new Zend_Filter_Encrypt($this->_encryption);
$encodeString = $filter->filter(Zend_Json::encode($p_data));
return bin2hex($encodeString);
}
public function decode($p_data)
{
$filtreChaine = new Zend_Filter();
$filtreChaine->addFilter(new My_Filter_HexToBin())
->addFilter(new Zend_Filter_Decrypt($this->_encryption));
$decrypted = trim($filtreChaine->filter($p_data));
return Zend_Json::decode($decrypted);
}
}
déjà, on remarque que c’est la même classe qui encode et décode. C’est bien plus simple de « faire » et « défaire » aux même endroit.
Qui mieux que la classe Images sera à même de décrypter ce qu’elle a crypter ?
Dans la fonction décode, on chaine 2 filtres, le premier, celui qu’on a crée, pour transformer l’hexa en binaire, et le second, celui pour décrypter.
Il ne reste plus qu’a appeler la méthode filter et le framework se charge d’appeler les 2.
L’objectif est atteins je crois. Nous avons fait connaissance avec les classes Zend_Filter_Encrypt/Decrypt et créer un filtre.
Pour ceux qui veulent encore plus sécuriser leur appli, l’étape d’après, au lieu de passer par mcrypt, c’est de jouer avec openssl et les certificats public / privé.
Super post !
J’ai eu le même soucis est je suis passé en base 64 et j’ai en effet eu de gros problème.
Par contre pourquoi passé par une fonction hex2bin quand on peut utiliser
pack("H*" ,$value);A plaisir de vous lire.