[spip-dev] Sessions hacking ...

Pas eu non plus le temps d'éplucher tout ce qui c'est dit mais voici un bon
article sur l'authentification par cookie ou comment générer des id non
falsifiable.

Dos and Don'ts of Client Authentication on the Web
http://pdos.lcs.mit.edu/cookies/pubs.html

http://pdos.lcs.mit.edu/cookies/pubs.html
France | Let There Be Change | Accenture

Ouah.

J'ai bien envie de sécuriser le hash_env() avec la liste des globales
fournies par Nicolas, au moins pour une période de tests. Les hardis
beta-testeurs nous diront si ça entraîne un niveau insupportable de
déconnexions inopinées.

L'alternative est de n'autoriser qu'une session par auteur (donc un seul
brouteur authentifié à la fois) : comme ça, si un voleur réussit à passer
entre les mailles du changement jajascript de session, le vrai auteur tue le
voleur dès sa reconnexion.

Il reste à élucider cette histoire de sniffage réseau vs chourage de
md5(pass) directement dans la base. Je suis convaincu que c'est l'un ou
l'autre (c'est mathématique!) ; mais j'aimerais y croire encore, comme dit
la chanson. En fait je crois que je préfère le risque de me faire sniffer le
md5(pass) par réseau (démarche très active de la part du pirate), plutôt que
par un oeil distrait jeté sur un dump de la base (démarche +- passive du
pirate).

-- Fil

J'ai bien envie de sécuriser le hash_env() avec la liste des globales
fournies par Nicolas, au moins pour une période de tests. Les hardis
beta-testeurs nous diront si ça entraîne un niveau insupportable de
déconnexions inopinées.

Oh non ;)) Le risque n'est pas un niveau "insupportable", c'est un niveau
non nul, et il est évident que ça arrivera (il suffit d'avoir un proxy
tournant).

L'alternative est de n'autoriser qu'une session par auteur (donc un
seul brouteur authentifié à la fois) : comme ça, si un voleur réussit à
passer entre les mailles du changement jajascript de session, le vrai
auteur tue le voleur dès sa reconnexion.

Peu pratique ;((

Je suis convaincu que c'est l'un ou
l'autre (c'est mathématique!)

Moi aussi.

plutôt que par un oeil distrait jeté sur un dump de la base
(démarche +- passive du pirate).

Tu trouves ça passif ? Faut déjà avoir accès à un dump, comment tu fais ?
(à part être pote avec l'administrateur de la machine :-))

L'alternative est de n'autoriser qu'une session par auteur (donc un
seul brouteur authentifié à la fois) : comme ça, si un voleur réussit
à passer entre les mailles du changement jajascript de session, le
vrai auteur tue le voleur dès sa reconnexion.

Peu pratique ;((

Et de toute façon le vrai auteur tue _déjà_ le voleur dès sa connexion
avec le même cookie. Donc intérêt quasi nul.

Oh non ;)) Le risque n'est pas un niveau "insupportable", c'est un niveau
non nul, et il est évident que ça arrivera (il suffit d'avoir un proxy
tournant).

Il tourne si souvent que ça le proxy tournant ?

> L'alternative est de n'autoriser qu'une session par auteur (donc un
> seul brouteur authentifié à la fois) : comme ça, si un voleur réussit à
> passer entre les mailles du changement jajascript de session, le vrai
> auteur tue le voleur dès sa reconnexion.

Peu pratique ;((

Faut savoir ce qu'on veut.

Tu trouves ça passif ? Faut déjà avoir accès à un dump, comment tu fais ?
(à part être pote avec l'administrateur de la machine :-))

Ou être sur la même machine, ou hériter d'un disque dur abandonné, ou être
dans la société de services qui fait des sauvegardes, ou qui installe
spip... etc. Et dans tous ces cas-là c'est passif (ie quand tu veux), alors
que le sniffage réseau doit se produire avec ta volonté au moment où la
victime se loge.

(Sinon, oui, les petits meurtres entre amis sont sûrement le risque majeur
des sites webs. Imagine un chevènementiste infiltré bénévolement sur le
site de campagne de Robert Hue...)

-- Fil

@ Antoine Pitrou <antoine@rezo.net> :

>> L'alternative est de n'autoriser qu'une session par auteur (donc un
>> seul brouteur authentifié à la fois) : comme ça, si un voleur réussit
>> à passer entre les mailles du changement jajascript de session, le
>> vrai auteur tue le voleur dès sa reconnexion.
>
> Peu pratique ;((

Et de toute façon le vrai auteur tue _déjà_ le voleur dès sa connexion
avec le même cookie. Donc intérêt quasi nul.

Non, si tu me voles mon cookie de session, tu en obtiens un nouveau illico.
Ma session meurt, je me reloge... et nos deux sessions se suivent en
parallèle sans se déranger.

Autre bug actuellement : on a oublié de vrérifier, quand on crée une
session, que ce numéro n'existe pas. Ce qui risque d'arriver souvent sur
windows où le random() n'offre que 32000 possibilités. 30 personnes qui
cliquent toutes les 10 secondes sur le site, ça fait à chaque clic 1 chance
sur 1000 de tomber sur une session existante, donc une chance sur 100, à
chaque seconde, d'avoir un trasfert d'identité d'une personne à l'autre.
Même sans volonté de pirater.

-- Fil

Il tourne si souvent que ça le proxy tournant ?

Beaucoup dans les banlieues.
Non, sérieux, à chaque requête a priori :wink:

Peu pratique ;((

Faut savoir ce qu'on veut.

A mon avis, on veut un truc pratique ;))

Ou être sur la même machine,

Elle est censée être sécurisée => pas le problème de SPIP.

ou hériter d'un disque dur abandonné

Administrateur système jmenfoutiste => idem :wink:

être dans la société de services qui fait des sauvegardes,

La société de service qui fait des sauvegardes, avec un peu de chance
fait aussi du transit Internet et peut sniffer les requêtes HTTP.

(Sinon, oui, les petits meurtres entre amis sont sûrement le risque
majeur
des sites webs. Imagine un chevènementiste infiltré bénévolement sur le
site de campagne de Robert Hue...)

J'ose à peine imaginer :)) Sûr qu'il faudrait finir par les séparer
physiquement :)))))

a+

Deux chapitres supplémentaires à ce fil déjà longuet.

VOL (au dessus d'un nid) DE COOKIE

Fil wrote:

Bon, on peut passer les numéros IP dans un filtre qui leur supprime le
dernier bloc : 193.3.41.7 -> 193.3.41.
Les IP tournantes ne tournent pas dans plus qu'une classe C je suppose ?

Pfouuu, on va pas chercher toutes les bidouilles imaginables pour essayer
de rendre peu probable un comportement qui est totalement autorisé par les
standards ? Qui acceptera de se taper le débuggage pour le pauvre gars
qui va tomber sur l'exception non prévue dans l'arsenal probabiliste ;))
Et, autant les incompatibilités facilement détectables, ça va (type "il
faut des cookies"), autant les subtilités totalement hors de contrôle,
ça fatigue vite à la fois l'utilisateur et le développeur.

Encore une fois, l'exemple des rubriques : un utilisateur dit que ça lui
crée des rubriques parasites, personne ne le croit (c'est un neuneu, ou
il est fatigué), puis deux trois autres pareil, un développeur se lève
et se dit qu'il faut aller voir ça, réussit à reproduire de temps en temps
le comportement mais de façon bizarrement aléatoire, finit par pondre un
patch qui marche chez lui mais s'avère foireux chez d'autres, et il faut
encore un bout d'investigations d'une autre personne pour en venir à bout.
Et encore, là, on a eu de la chance : le bug était reproductible chez moi
et Arno....

Honnêtement : l'exception, elle *va* arriver. Ce jour-là ou plutôt ce
mois-là, après avoir bien cherché et s'être dit (au bout de quelques
jours de questions insistantes et de réponse embarrassées) que c'était
peut-être bien le truc de l'authentification, on va finir par désintaller
définitivement le truc de l'authentification. Autant le faire tout de suite :wink:

(Au fait :
antoine@miel:~$ host ns3.gandi.net
ns3.gandi.net A 212.73.209.246
antoine@miel:~$ host ns4.gandi.net
ns4.gandi.net A 80.67.173.194

Pas un proxy, mais c'est quand même deux machines au rôle très similaire,
et elles sont même pas dans le même /8.)

Autre réponse au vol de cookie par jaja : bloquer l'exécution de tout type
de javascript. S'il existe des interpréteurs javascripts, c'est qu'il y a un
nombre fini de choses à vérifier dans interdire_scripts(). Non ? C'est pas
hors-contrôle, le jajascript ?

Le problème, c'est qu'on finit par interdire des tas de trucs, ce qui est
lourd et risque d'être gênant pour ceux qui écrivent des articles (exemple
typique : j'écris une initiation à javascript). D'autre part, vu (à vue
de nez) le nombre de machins qui peuvent provoquer l'exécution d'un jajastruc
(type onLoad, onMouseOver, onGrillePainExhausted....), vu d'autre part qu'il
n'y a aucune raison que des ajouts à jajastruc n'introduisent des failles
de sécurité supplémentaire par la suite, vu que le tout est multiplié par
le nombre de brouteurs du marché, autant ne pas se faire chier. Le
problème, c'est les fabricants de brouteurs qui ont décidé que c'était
intelligent de rendre les cookies lisibles en javascript (ils ont oublié
l'adresse e-mail et le mot de passe administrateur...).

Tiens, au fait, dans Mozilla, on peut désactiver sélectivement la lecture et
l'écriture de cookies en Javascript (tout en l'autorisant par les méthodes
normales). Ca résoud le problème, pour les paranos ;))

En fait, je crois que ce qui me gêne dans la situation que tu proposes (le
challenge), c'est qu'un accès en lecture à la BDD, ne serait-ce qu'une fois,
suite à une négligence quelconque, donne une porte d'entrée sur le site
PERMANENTE (sauf à changer TOUS les mots de passe de TOUS les admins).

Ben, disons que c'est surtout un problème qui te touche toi, mais la majorité
des sites SPIP sont hébergés chez des mutualisés où la config du serveur opère
un très bon cloisonnement, et donc il n'y a aucune raison que la base soit
accessible de l'extérieur suffisamment durablement pour qu'un type, après être
arrivé par hasard (???!!) ait la présence d'esprit de noter les md5 dans un coin
pour les réutiliser. Par contre, sur le serveur mutualisé typique, pas de HTTPS
(et pour cause : on ne peut pas faire de Named Virtual Host en HTTPS, il faut
donc une IP différente par hôte HTTPS....) - de plus, HTTPS bouffe beaucoup de
temps machine s'il y a beaucoup de requêtes.

Et de même, si quelqu'un réussit à lire le /etc/shadow sur ta machine Linux,
tu as intérêt à changer les mots de passe de tous les utilisateurs privilégiés
(les autres, tu les préviens juste et tu leur dis qu'"il vaut mieux").

Enfin, ce que je pense, ce serait bien que d'autres donnent leur avis aussi ;))

Amicalement

Antoine.

Coucou,

J'ai un nouvel argument, qui cette fois devrait l'emporter : on ne PEUT PAS
se protéger contre le sniffage réseau autrement qu'en https. Parce que si tu
sniffes mes connexions, tu lis mon cookie au moment où je l'envoie (le
cookie tournant limite les dégâts) mais aussi... au moment où je le reçois !!
Et là, tu as le temps qu'il faut pour l'utiliser et chopper ma session. Donc
le bon choix dans notre alternative est le double md5 dans la base, et pas le
challenge.

Sinon, quelques détails :

> Les IP tournantes ne tournent pas dans plus qu'une classe C je suppose ?
Pfouuu

OK, OK.

Tiens, au fait, dans Mozilla, on peut désactiver sélectivement la lecture et
l'écriture de cookies en Javascript (tout en l'autorisant par les méthodes
normales). Ca résoud le problème, pour les paranos ;))

Ah bin voilà !!

> En fait, je crois que ce qui me gêne dans la situation que tu proposes
> (le challenge), c'est qu'un accès en lecture à la BDD, ne serait-ce
> qu'une fois, suite à une négligence quelconque, donne une porte d'entrée
> sur le site PERMANENTE (sauf à changer TOUS les mots de passe de TOUS
> les admins).

Ben, disons que c'est surtout un problème qui te touche toi, mais la

N'oublie pas les petits meurtres entre amis.

Enfin, ce que je pense, ce serait bien que d'autres donnent leur avis aussi
;))

Pareil. D'autant qu'à force d'en débattre, ça devrait commencer à être
compréhensible (pour ceux qui suivent encore ;-] )

-- Fil

Bonjour,

Enfin, ce que je pense, ce serait bien que d'autres donnent leur
avis aussi ;))

Pareil. D'autant qu'à force d'en débattre, ça devrait commencer à
être compréhensible (pour ceux qui suivent encore ;-] )

Bin non, ça commence pas forcément à devenir compréhensible,
justement, mais bon ... :stuck_out_tongue:

De toute façon, pas le temps de regarder avant demain,
malheureusement.

-Nicolas

Euh...

M'enfin. Mon avis (pour avoir expérimenté), c'est que
- soit on encrypte la session via HTTPS,
- soit on reprend un auth-realm classique.

Hormis ce s deux solution, rien ne sera sécure. Et les sources étant
dispo, le pirate qui voudra, pourra.

Pfou, bon.... Je viens de griller quelques neurones en prenant ma douche :((

Soit P le mot de passe en clair (connu du seul auteur).

Dans la base, on stocke :
- $row["alea"] : un aléa (dingue)
- $row["pass"] : md5(P . $row["alea"])

($row["alea"] initialisé à une chaîne vide pour faciliter l'upgrade de la base)

Formulaire de login... en deux temps côté client :

1. Demande de login : le formulaire envoie le login en clair

2. Le PHP stocke le login en cookie et renvoie un formulaire
contenant :
- $ancien_alea : $row["alea"] correspondant au login
- $nouvel_alea : un alea généré et différent de $ancien_alea
- $id_auteur (ou $login, enfin comme on veut)

3. Le formulaire demande le mot de passe supposé (K) et renvoie :
- $ancien_pass = md5(K . $ancien_alea)
- $nouveau_pass = md5(K . $nouvel_alea)
- $nouvel_alea
- $id_auteur (ou $login, enfin comme on veut)

4. Le PHP compare $ancien_pass avec la valeur stockée dans la base,
et vérifie que $ancien_pass != $nouveau_pass et $nouvel_alea != $row["alea"].
Si c'est ok :
- mise à jour atomique de la base :
~ UPDATE spip_auteurs WHERE (...) SET pass='$nouveau_pass', alea='$nouvel_alea'
- l'auteur est authentifié (cookie session posé)

Tout ce que peut faire un attaquant, que ce soit en sniffant le
réseau ou la base : récupérer la paire ("pass", "alea"). Il peut
donc bidouiller un formulaire de login qui renvoie le bon $ancien_pass.
Malheureusement, il est obligé également de générer un $nouveau_pass
différent de l'ancien (sinon le login est refusé), et n'ayant pas
connaissance du véritable P il est obligé de foirer la base. Au
prochain login de l'auteur véritable, P sera refusé et l'auteur
demandera donc à l'administrateur de rétablir le mot de passe.
Non seulement l'attaquant ne pourra pas plus s'identifier, mais
le fait que le mot de passe ne passe plus signale un "problème"
éventuel ;))

Le seul truc, le formulaire est en deux temps (il faut la donnée
du $login pour renvoyer ensuite le $ancien_alea associé). Mais
en stockant le $login dans le fameux cookie qui dure mille ans,
on peut sauter direct à la deuxième étape dans la majorité des
cas.

Des avis ? Je vais manger mes spaghetti.

Des avis ? Je vais manger mes spaghetti.

Excellent ! J'espère qu'il y a moins de harissa dans tes pâtes que dans ton
shampooing dis-donc !

-- Fil

Correction : il faut stocker le $nouvel_alea dans la base, sinon
l'attaquant peut :
1- se logger avec un ($nouvel_alea, $nouveau_pass) foireux
2- se relogger tout de suite après avec le couple ($ancien_alea,
$ancien_pass) sniffé précédemment, afin de camoufler la supercherie.

En vérifiant que le $nouvel_alea utilisé correspond bien à celui
envoyé par PHP, on évite le rejeu de l'ancien couple.

@ Antoine Pitrou <antoine@rezo.net> :

Pfou, bon.... Je viens de griller quelques neurones en prenant ma douche :((
Dans la base, on stocke :
- $row["alea"] : un aléa (dingue)
- $row["pass"] : md5(P . $row["alea"])

Si ça ne troue rien, je suggère qu'on garde le $row['pass'] intact, et que
le mot qui bouge soit dans $row['passmobile']. Ainsi, si jamais l'attaquant
réussit à foirer la base, tu peux revenir récupérer ton compte en te
connectant en auth http... puis en modifiant ton mot de passe (en espérant
que cette fois-ci tu n'es pas sur écoutes !)

Par ailleurs, ça nous assurerait de ne pas planter les mots de passe si
jamais... on s'aperçoit que tous ces commits qui se préparent sont en fait
un gros tas de boue. :wink:

Correction : il faut stocker le $nouvel_alea dans la base, sinon
l'attaquant peut :
1- se logger avec un ($nouvel_alea, $nouveau_pass) foireux
2- se relogger tout de suite après avec le couple ($ancien_alea,
$ancien_pass) sniffé précédemment, afin de camoufler la supercherie.

Ouah, j'y avais pas pensé ! Je ne sais pas si tout ça va s'avérer très
opérationnel (pour avoir juste un cookie d'authentification dans l'espace
public, ouarf!)... mais c'est au moins très instructif et marrant.

-- Fil

> Correction : il faut stocker le $nouvel_alea dans la base, sinon

en fait $nouvel_alea = $alea + 1 doit suffire, non ?

-- Fil

Fil wrote:

en fait $nouvel_alea = $alea + 1 doit suffire, non ?

Je ne pense pas, car tu pourrais rejouer sur un autre site
les paires sniffées sur un site. Tu pourrais aussi détecter
qu'un mot de passe est le même si tu vois une paire que tu
connais.

Si ça ne troue rien, je suggère qu'on garde le $row['pass']

??? Ben s'il est figé, bien sûr que ça troue (niveau sniffing
de base). Et puis le truc de l'authentification de secours, ça
devient très tordu vis-à-vis de l'utilisateur moyen.

a+

??? Ben s'il est figé, bien sûr que ça troue (niveau sniffing
de base).

Euh. Si tu postes $alea='' et $md5pass = "ce que j'ai vu dans la base", spip
ne te croira pas.

Et puis le truc de l'authentification de secours, ça
devient très tordu vis-à-vis de l'utilisateur moyen.

C'était un exemple, certes assez idiot. Disons que ça permet de faire un
retour en arrière d'une version de spip si notre jolie conception se casse
la figure pour une raison encore inconnue à ce jour.

Enfin, là, je ne me battrai pas. Ta construction me paraît bonne. Même si je
me dis "C'est pas possible on doit se planter", vu que personne ne semble
faire ce genre de trucs.

PS: quant à implémenter, euh, je passe, sauf si tu veux t'amuser à me voir
faire des essais/erreurs. Je vais tout de même virer un truc inutile dans
login.php3, et ajouter un truc inutile (un fond d'écran !)

-- Fil

C'était un exemple, certes assez idiot. Disons que ça permet de faire un
retour en arrière d'une version de spip si notre jolie conception se casse
la figure pour une raison encore inconnue à ce jour.

Heu, je veux bien, mais tu veux mettre quoi exactement dans le "passpasmobile" ?

De toute façon, on a des sites de test.... :wink: