La gestion des dates

S’il est un sujet qui me tient énormément à cœur depuis que j’ai découvert d’un côté l’API DateTime de PHP et d’un autre côté celle de JAVA au court du développement d’une application Android, c’est la gestion des dates lorsqu’on est sur le web.
De la gestion des “Il y a un jour” à  la facebook, jusqu’à  l’affichage des dates dans toutes les langues, la nécessité de la gestion des dates est omniprésente.

Quand mes errances sur les dates ont-elles commencé? A la fin de mon projet de première année de Cycle Informatique et Réseau (CIR), je me suis rendu compte que le script que j’avais produit avait non seulement été très long à  produire, mais qu’en plus il n’était pas du tout optimisé.
Premièrement j’utilisais le timestamp partout, même dans ma bdd. A priori c’est une grosse erreur. J’aimerai néanmoins apporter quelques nuances à  cette pensée :

  • Le timestamp est très compact (32 ou 64 bits selon le système d’exploitations) face au type DateTime (aaaa-mm-dd hh:ii:ss) qui prend un minimum de 6 octets.
  • un UNIX_TIMESTAMP est insensible au fuseau horraire, contrairement au type DateTime de MySQL

Vous pourrez trouver toutes les différences techniques du type DateTime de SQL sur ce topic de stackoverflow.

Dans le cas particulier de mon projet de première année, je devais gérer un système multilingue. Ce qui est rapidement casse pied avec un simple timestamp (même quand on optimisait le script que j’avais créé), ne serait-ce que parce que chaque pays à  sa manière propre d’afficher une date, indépendamment de la traduction des jours.
A ce jeu là , le timestamp est vraiment mauvais contrairement aux types plus complexes (DateTime en php, Date en JavaScript, Date enJava…) qui sont vraiment des bouffées d’air frais. Non seulement ils ajoutent du sens au programme, mais en plus ils donnent accès à des API bien plus complètes !
Pour l’exemple PHP posséde un formateur de date international IntlDateFormatter]qui fonctionne à  la perfection (nécessite l’extension intl).

Mais souvenons-nous du cas de l’affichage “mode facebook” (qui est un cas particulier de calcul finalement).
Bien des gens aiment à  faire des calculs (assez imprécis) pour tester l’antériorité (une minute, une journée, par exemple) alors que la notion d’intervalle suffit elle même à  contenir tous ces tests. Dans le cas particulier de php, ce code affichera une date “à  la facebook” :
[cc lang=”php”] $today = new DateTime;
$bestDayOfYear = new DateTime(‘2012-02-27’);
$interval = $today->diff($bestDayOfYear);
$array = ["d"=>"jours", "y"=>"années", "m"=>"mois", "h"=>"heures", "i"=>"minutes","s"=>"secondes"];
$facebook ="";
foreach((array)$interval as $key=>$value){
if($value > 0){
if(isset($array[$key])){//php ajoute un champ qui ne nous intéresse pas
$facebook .= $value. $array[$key];
}
}
}
echo $facebook;[/cc]

Si ce n’est le fait que php ajoute un champ “days” dans interval, il faudra avouer que ce code a l’avantage d’être extrêmement lisible et d’avoir un sens totalement transparent !

Je dois aussi avouer que lorsque j’ai dû faire, durant ce projet, l’élaboration de mon calendrier a été sûrement ce qui m’a le plus posé de difficultés. En effet n’ayant ni connaissance en javascript à  l’époque (et n’ayant pas connaissance des datepicker déjà  codés) ni la possibilité (les consignes, tout ça) d’utiliser ce genre de script, j’avais recodé un calendrier/datepicker.
Le jour où j’ai découvert qu’écrire le code suivant suffisait, j’ai pleuré :
[cc lang=”php”]$debutCal = new DateTime("last monday of previous month");
$finCal = new DateTime("last day of this month");
if($finCal->format("D") !=="Sunday"){
$finCal->modify("first Sunday of next month");
}
$iterateur = new DatePeriod($debutCal,new DateInterval(‘P1D’),$finCal);
foreach($iterateur as $date){
mettreEnFormeMaDate($date);//selon le design…
}
[/cc]

Un des autres avantages est clairement l’interopérabilité des types. Entre DateTime de MySQL et celui de PHP, il n’y a pas besoin d’adaptation alors qu’il faut passer par strtotime pour avoir un timestamp.
Un des utilisateurs de StackOverflow me donnait justement une réponse tout à  fait détaillée à  propos des exigences auxquelles il avait affaire dans des communication entre php et .NET :

I mentioned before about interop between our PHP systems and .NET systems. Whilst there are no specific issues caused by using a timestamp it’s simply not the practical solution, as again, we use a database that returns a DateTime value which can be sent straight down the pipe. If we were to convert this to a unix timestamp for use internally in PHP we’d also have to then convert it back out if we were to send a response, or send a response to the .NET Application (or should I just say API in this case) that is a timestamp, and convert it at the end. By using DateTime across the board, it alleviates the need for any conversions to happen whatsoever and the whole development process is easier.

S’il est vrai que je suis très favorable aux objets complexes du type DateTime, j’arrive néanmoins à trouver encore des petites utilisations au timestamp :
-marquer temporellement la création d’un fichier/d’un commentaire pour faire des ancres de liens
-simple système affichage/transfert de date

Néanmoins la limite supérieur de dynamique qui est placée à  2038 est vraiment handicapante. Il est aussi à  noter que contrairement à  ce qui est souvent dit, le timestamp est un entier signé (ce qui explique les 68 ans de dynamique (environ 2 000 000 000 de secondes) avancés le plus souvent. En fait on peut aller encore 68 ans en arrière en prenant des timestamp négatifs :
[cc lang=”php”]
echo date(‘d/m/Y’,-1);//prints 31/12/1969
[/cc]

sécuriser les mots de passe

Hier, les développeurs de PHP ont entériné par un vote unanime (chose rare !) la création d’une API simplifiée de hashage de mot de passe. C’est une occasion en or pour rappeler quelques bons principes en ce qui concerne la sécurité des données de vos utilisateurs

Etre sécurisé, techniquement

Lorsque l’on développe un système qui nécessite l’authentification d’un utilisateur pour que celui-ci puisse consulter des données qui lui sont réservées, vient toujours à un moment la considération “que fais-je du mot de passe”?

Le choix du clair

Certains sites comme skyrock (hébergeur des skyblog) ont décidé… d’enregistrer en clair les mots de passe dans leur bdd et de séuriser “un maximum” leur bdd en amont. L’intérêt qu’ils y voient c’est la facilité de retrouver une mot de passe que l’utilisateur aurait perdu.

Oui, mais… ce n’est pas du tout sécurisé. Si le mot de passe est stocké en clair, ça signifie que quiconque (employé, pirate suffisamment bon pour obtenir un bout de la bdd…) peut obtenir sans effort le mot de passe de l’utilisateur et aller fouiller dans ses données personnelles. Les gens de skyrock relativisent en disant que de toute façon, les données enregistrées dans leur bdd sont toutes, ou presque, publique, donc pas d’inquiétude.
Plusieurs problèmes se posent :
Premièrement, j’y reviendrait dans la seconde partie de mon billet, un mot de passe n’ouvre pas que l’accès à  skyrock pour un pourcentage d’utilisateur fort élevé vu l’audience visée par le site.
Secondement, skyrock vous dit “votre mot de passe est sécurisé car c’est nous qui le gardons secret et ne le communiquons qu’avec vous”. Oui mais c’est ce qu’on appelle une pétition de principe : le mot de passe est sécurisé parce que nous gardons le secret… donc nous partons du principe que le secret est gardé. Or je le rappelle les attaques contre lesquelles nous voulons nous prémunir sont un pirate qui force la bdd ou un employé qui recopie la bdd pour  son usage personnel, en gros une violation du secret…

RSA : chapeau

Viennent ensuite les gens qui veulent crypter, ou chiffrer comme on dit en France. RSA ne signifiant plus ici une allocation mais un algorithme asymétrique de cryptage des données. Asymétrique parce qu’il utilise une clef privée (encore un secret à  garder) pour décrypter les messages.

Mais que signifie “crypter”? Ca signifie que toute donnée est transformée de manière unique en une autre donnée afin de rendre “incompréhensible” le message pour quoi n’a pas le code pour la transformation inverse.

Car transformation inverse il y a. Donc pour peu que le secret ait été éventé (bien qu’il soit plus facile de cacher un fichier de clef qu’une bdd), le code reste donc traductible, c’est comme si on avait le mot de passe en clair.
Sauf qu’on fait consommer du temps de calcul à  l’ordinateur. Ca tombe bien, c’est ce qu’on désire faire en fait : forcer le pirate à  prendre tellement de temps que ça n’est pas rentable pour lui d’aller essayer de récupérer notre mot de passe.
Alors, certes, les algo de cryptage prennent des ressources, mais, malheureusement, ils ne nécessitent qu’un essai pour être craqué une fois que le secret a été éventé. La bruteforce n’est pas utile et donc finalement, prendre 500ms au lieu de 5, le pirate s’en fiche.

Du sel? Du poivre? dans vos épinard hashés

La “solution” est donc le hashage, ou signature en français.
Le hashage fonctionne de cette manière :
il prend votre chaîne et la transforme en une chaîne de longueur donnée, 32 caractères pour md5 par exemple.

Il faudra se souvenir que ces caractères sont des lettres minuscules ou des chiffres.

A partir de ces deux précisions, vous constaterez que deux mots de passe différents peuvent avoir la même signature, c’est ce qu’on appelle une collision.
Heureusement, face au panel de mots de passe réellement utilisés par les gens, les collisions sont très rares, néanmoins à  cause de ses 32 caractères, des dictionnaires ont déjà  été créés pour md5 afin de créer des collisions.

Une solution envisageable est de mettre un grain de sel (ou un grain de poivre) dans votre mot de passe : c’est à  dire qu’avant hashage, vous faites muter votre mot de passe en y ajoutant une certaine chaîne. La nuance entre sel et poivre n’est ici pas le goût mais le fait que le sel est public (souvent stocké en bdd) alors que le poivre est gardé secret (toujours la même chose, il est secret tant que le secret n’est pas éventé). Cela permet de ralentir la création de tables.

On peut alors penser à  utiliser des algorithmes comme sha1, sha512, samba2, tiger…
Le problème de ces algorithme est qu’ils sont “sensibles” à  l’attaque par force brute. sha1 a déjà  été largement déconseillé à  cause de ça, sa rapidité d’exécution fait qu’il est facile à “bruteforcer”. Les autres sont pour l’instant relativement coûteux et donc peuvent être utilisés, mais ils vont devenir obsolète un jour ou l’autre à cause de la loi de Moore :

Le nombre de transistors des microprocesseurs sur une puce de silicium double tous les deux ans

Cela signifie que les algorithmes dont je vous parle, s’ils sont impossibles à  mettre dans des dictionnaires ou tables en arc-en-ciel finiront par être déverrouillés très rapidement par la monté en puissance des ordinateurs.

Alors que faire?
Il existe un algorithme, souvent appelé blowfish qui est tout à  fait particulier. Ce dernier ne prend pas simplement un mot de passe et un grain de sel comme paramètre, mais aussi une “complexité”. Cette complexité définit en fait la quantité de temps que devra mettre l’algorithme pour donner son résultat. Plus le temps est élevé, plus l’algorithme est coûteux. Mon expérience me dit que la complexité est souvent donnée par le chiffre 12, sinon la génération du hashcode peut devenir trop coûteuse, mais bon chacun voit midi à  sa porte et je n’ai pas envie de créer de débat là  dessus.

En php on utilise la fonction crypt pour appeler blowfish.
Elle s’utilise en donnant comme paramètre de sel une chaîne de caractère formée ainsi :

  • [ul]$2a$ ou $2x$ ou $2y$ depuis 5.3.7
  • deux chiffres pour la complexité, donc 07 pour 7
  •  un $
  • 22 caractères alphanumériques puis un $

Par exemple, dans la donc on nous propose

[cci lang=”php”]echo crypt(‘rasmuslerdorf’, ‘$2a$07$usesomesillystringforsalt$’) ;[/cci]

En Python on trouve :
[cc lang=”python”]import bcrypt
#la complexité est ici de 10, par défaut c’est 12
hashed = bcrypt.hashpw(password, bcrypt.gensalt(10))[/cc]

Etre sécurisé : socialement

La plus grande faille de sécurité, ce sont vos utilisateurs. Ils utilisent souvent le même mot de passe pour plusieurs voire tous les sites sur lesquels ils sont inscrits. Donc trouver un mot de passe dans votre bdd, aussi peu importante soit-elle, ça peut signifier le piratage de comptes plus importants pour ces utilisateurs.

Vient ensuite un autre problème : la mémoire des utilisateurs. Un mot de passe complexe, tel @fçz59fo est complexe à  retenir, sans pour autant être complexe à  trouver pour un ordinateur qui essaierait un peu de bruteforce. En effet trouver une chaîne de 9 caractères c’est relativement facile pour un ordinateur, et puisque ce dernier n’aura pas l’idée d’aller essayer le nom du chien, le surnom de la première voiture de l’utilisateur que l’on veut pirater comme le font les espions américains dans nos séries préférées, la “difficulté” du mot de passe est un peu surfaite.
Un 9gag est devenu assez célèbre à  ce propos, qui disait “Durant ces 10 dernières années nous avons formé les gens à  faire des mots de passe impossibles à  retenir pour l’homme mais facilement devinables pour un ordinateur”. Quel dommage.

TL;DR

 

 

blowfish sur un mot de passe suffisamment long, mais pas forcément avec des caractères spéciaux, tu utiliseras, et tes données sécurisées seront