Auto_restrict : un fichier pour les verrouiller tous.

Pour continuer cette série sur la simplification des trucs ch****, je vous propose un fichier php qu'il suffit d'inclure dans une page pour en restreindre l'accès.


Comme de coutume, un cahier des charges:

  • simplicité d'utilisation,
  • configuration aisée,
  • sécurité élémentaire (sessions)
  • gestion automatique de l'expiration de session pour inactivité.

Les commentaires sont les bienvenus, je ne suis pas un expert de la sécurité ;) (et ça se voit )

 


[Edit du 27/10/2012]

Ajout d'une plus grande sécurisation via un snippet proposé par JérômeJ (thanxalot mon pot')

 

 


 

Le zip contient un exemple complet et simple:

  • un fichier index.php qui appelle auto_restrict.php
  • un fichier login_form.php contenant le formulaire servant à se logger
  • auto_restrict.php (le fichier qu'il suffit d'inclure)

Lors de la première utilisation du script, Auto_restrict va proposer de créer le passe via un formulaire: on tape le passe voulu et le fichier pass.php est créé automatiquement avec le passe crypté.

Lorsqu'on charge ensuite la page qui appelle auto_restrict, cette dernière vérifie si les variables de session sont présentes et si elles sont correctes.

Afin d'éviter de se faire pécho sa session, j'ai ajouté une variable contenant toutes les infos de l'utilisateur (ip+navigateur) cryptée; j'ai récupéré les fonctions de cryptage sur info-3000.com merci au passage .

Si un problème est détecté, on dégage les variables de session éventuelles, on la détruit, on include le formulaire de login et on arrête tout.

 

Quand on remplit le formulaire et qu'on l'envoie, c'est encore auto_restrict qui prend la main et gère la connexion: si les login/passe correspondent à ceux fournis en config, on crée la session, sinon, on considère qu'il y a un problème... et on include le formulaire

    session_start();
// ------------------------------------------------------------------
// configuration    
// ------------------------------------------------------------------
$auto_restrict['error_msg']='Erreur - impossible de se connecter.';// utilisé si on ne veut pas rediriger
$auto_restrict['encryption_key']='abcdef';// clé pour le cryptage de la chaine de vérification
$auto_restrict['session_expiration_delay']=1;//minutes
$auto_restrict['login']='login'; // caractères alphanum + _ et .
$auto_restrict['redirect_error']='index.php';// si précisé, pas de message d'erreur


// ---------------------------------------------------------------------------------
// sécurisation du passe: procédure astucieuse de JérômeJ (http://www.olissea.com/)
@include('pass.php');
if(!isset($auto_restrict['pass'])){
    if(isset($_POST['pass'])){ # Création du fichier pass.php
        $salt = md5(uniqid('', true));
        file_put_contents('pass.php', '<!--?php $auto_restrict["salt"] = '.var_export($salt,true).'; $auto_restrict["pass"] = '.var_export(hash('sha512', $salt.$_POST['pass']),true).'; ?-->');
        include('login_form.php');exit();
    }
    else{ # On affiche un formulaire invitant à rentrer le mdp puis on exit le script
        include('login_form.php');exit();
    }
}
// ---------------------------------------------------------------------------------


// ------------------------------------------------------------------

// ------------------------------------------------------------------
// gestion de post pour demande de connexion
// si un utilisateur tente de se loguer, on gère ici
// ------------------------------------------------------------------   
if ($_POST){log_user($_POST['login'],$_POST['pass']);}
// ------------------------------------------------------------------   
// si pas de demande de connexion on verifie les vars de session
// et la duree d'inactivité de la session
// si probleme,on include un form de login.
// ------------------------------------------------------------------
if (!is_ok()){session_destroy();include('login_form.php');exit();} 
// ------------------------------------------------------------------
// demande de deco via la variable get 'deconnexion'
// ------------------------------------------------------------------   
if (isset($_GET['deconnexion'])){log_user($_POST['login'],$_POST['pass']);}
// ------------------------------------------------------------------   

Pour la partie fonctions:

    function id_user(){
        // retourne une chaine identifiant l'utilisateur que l'on comparera par la suite
        // cette chaine cryptée contient les variables utiles sérialisées       
        $id=array();
        $id['REMOTE_ADDR']=$_SERVER['REMOTE_ADDR'];
        $id['HTTP_USER_AGENT']=$_SERVER['HTTP_USER_AGENT'];
        $id['session_id']=session_id();
        $id=serialize($id);
        return $id; 
    }
function is_ok(){
    // vérifie et compare les variables de session
    // en cas de problème on sort/redirige en détruisant la session
    global $auto_restrict;
    $expired=false;

    if (!isset($_SESSION['id_user'])){return false;}
    if ($_SESSION['expire']&lt;time()){$expired=true;}
            $sid=Decrypte($_SESSION['id_user'],$auto_restrict['encryption_key']);
    $id=id_user();
    if ($sid!=$id || $expired==true){// problème
        return false;
    }else{ // tout va bien
        //on redonne un délai à la session
        $_SESSION['expire']=time()+(60*$auto_restrict['session_expiration_delay']);
        return true;
    }
}


function log_user($login_donne,$pass_donne){
    //cree les variables de session
    global $auto_restrict;
    if ($auto_restrict['login']==$login_donne &amp;&amp; $auto_restrict['pass']==hash('sha512', $auto_restrict["salt"].$pass_donne)){
        $_SESSION['id_user']=Crypte(id_user(),$auto_restrict['encryption_key']);
        $_SESSION['login']=$auto_restrict['login']; 
        $_SESSION['expire']=time()+(60*$auto_restrict['session_expiration_delay']);
        return true;
    }else{
        exit_redirect();
        return false;
    }
}

function redirect_to($page){header('Location: '.$page); }
function exit_redirect(){
    global $auto_restrict;
    @session_unset();
    @session_destroy();

    if ($auto_restrict['redirect_error']&amp;&amp;$auto_restrict['redirect_error']!=''){//tester sans la deuxième condition
            redirect_to($auto_restrict['redirect_error']);
    }else{exit($auto_restrict['error_msg']);}
}
</pre>
<p>
    &nbsp;</p>
<p>
    Pour la page index.php</p>
<pre class="brush:php;">

<?php // on include et c'est tout ! include('auto_restrict.php');

?> <div class='content'> <h1>Auto_restrict.php</h1><hr/> Vous etes <em>connecté!</em><br/> Durée d'inactivité avant déconnexion:<em> <?php echo $auto_restrict['session_expiration_delay']; ?> min.</em>

&lt;fieldset&gt;
&lt;a href='http://www.warriordudimanche.net/index.php' &gt;Recharger cette page&lt;/a&gt;
&lt;a href='http://www.warriordudimanche.net/?deconnexion=ok' class="deco"&gt;DECONNEXION&lt;/a&gt;
&lt;/fieldset&gt;

</div>

 

 

 


On peut toujours complexifier le tout en 

  • sécurisant les données post,
  • en faisant un ban provisoire des ip+navigateur qui cherchent à se logger sans succès trop de fois, 
  • en gérant plusieurs logins/mdp (via un fichier xml ou une BDD...)

Toutefois, je ne voulais pas compliquer l'utilisation sans raison.

 

Comme toujours, le zip est par là et la démo ici (login=login et pass=pass !)

 

❝ 19 commentaires ❞

1  JeromeJ le

Y a un truc que je pige pas :


if (isset($_GET['deconnexion'])){log_user($_POST['login'],$_POST['pass']);}


Pourquoi le script de déconnexion est le même que celui de connexion :(


OMG les mots de passes sont stockés en clair :o
Ça peut se comprendre car ça tente d'être un KISS mais c'est pas non plus ce que j'appellerais sécurisé non plus alors
(Moi je sha512 + salt :p)

 
2  le hollandais volant le

Heu, une configuration de tout ça par htaccess ça marche aussi, et c’est plus simple : deux petits fichiers à créer (.htaccess & .htpasswd) et tous les fichiers du dossier et des sous dossiers sont automatiquement protégés.

 
3  Bronco le

Hinhin, je n'ai pas cherché à sécuriser le passe pour l'instant car ce n'était pas l'objet du truc: il conviendrait de toutes façons de fournir le passe au script via une fonction extérieure.


Pour ce qui est du htaccess, c'est vrai, en effet, mais j'ai toujours un peu de mal avec... c'est pas super user friendly ;)
En l'occurrence, c'est plutôt pour restreindre l'accès à une page précise sans se prendre la tête à gérer...


Dans le cahier des charges, la simplicité est en premier
Du coup, bien sûr, ce n'est pas optimisé pour d'autres objectifs
C'est dans ce but de simplicité que c'est le même script qui gère tout.

 
4  JeromeJ le

Un tit bogue d'affichage dans l'article :D je passe ptet au mauvais moment


Bref, j'ai du retard dans la lecture de tes articles/codes, tu postes trop :/ lol

 
5  Bronco le

@JeromeJ :
En effet, j'avais déplacé une sans m'en rendre compte Merci.
Je vais sans doute ralentir un peu les posts les prochains jours, cause vacances.
A moins que je vous prépare les articles de suite comme je fais depuis le début (j'ai toujours une semaine d'avance)

 
6  JeromeJ le

Tu me permets ? :p J'aime chicaner :$


Je trouve ça bizarre que sha512 te renvoie si peu de caractères (il m'en renvoie 128)


Personnellement je n'utilise pas crypt car son fonctionnement est assez spéciale (pour pas dire bizarre), la chaine qu'on lui passe comme premier argument n'est pas juste le mot de passe, elle est interprétée (un peu à la manière d'une regex (mouais)) donc "théoriquement" certains mots de passes risqueraient de coincer …


Quant au salt, il permet de lutter contre les attaques de type rainbow table :p
Plutôt que de compliquer encore le truc, je te passe mon bout de code :


$salt = md5(uniqid('', true)); # Peu importe ce que c'est tant que c'est assez aléatoire
$password = hash('sha512', $salt.'mon mot de passe'); # On place également le salt où on veut


Le script pourrait être adapté ainsi :


@include('pass.php');
if(!isset($auto_restrict['pass']))
{
if(isset($_POST['pass'])) # Création du fichier pass.php
{
$salt = md5(uniquid('', true));
file_put_contents('pass.php', '<?php $auto_restrict["salt"] = '.var_export($salt).'; $auto_restrict["pass"] = '.var_export(hash('sha512', $salt.$_POST['pass'])).'; ?>');
}
else # On affiche un formulaire invitant à rentrer le mdp puis on exit le script
}


Pour reset il suffit de vider ou supprimer pass.php.
C'est encore plus simple pour l'user (je trouve).


T'es pas obligé d'encore le changer x) c'est déjà un honneur si j'ai pu apporter ma ptite contrib, ça en aidera surement certains … j'espère !


Dernier truc à propos de crypt, sha512 n'a été rajouté qu'à partir de la 5.3 selon la doc alors que hash fonctionne à partir de 5.1.2 ^-^
Bon après j'ai pas vérifier en live si hash sha512 fonctionnait avant 5.3 :D




Ah et j'ai aussi remarqué un ptit truc sur ta page de démo, lorsqu'on se déconnecte, la page semble être d'abord chargée (donnant l'impression qu'on est toujours connecté) avant de vraiment nous déconnecter et la preuve qu'on est déco est qu'il suffit de charger n'importe quelle page pour remarquer qu'on a plus accès à rien (je le précise car j'ai d'abord naïvement cru qu'il fallait appuyer 2 fois sur déconnexion vu que la première fois semblait ne rien faire :p)

 
7  Bronco le

@JeromeJ :
Excellente analyse. Je vais y réfléchir mais du coup, comme tu m'apportes la soluce sur un plateau... c'est peinard pour moi ;)
Je suis assez noob en sécu (la plupart de mes applis sont très peu sécurisées parce que ... je suis le seul que ça intéresse en général )


Je changerai lors de la MAJ suivante, je pense.
Merci encore !

 
8  JeromeJ le

Derien, merci pour le backlink, c'est trop :D


Normal que t’aies mis '<!--?php ' au début et '?-->' à la fin ??
Chez moi ça donne qu'un commentaire HTML (non interprété même lors de l'include) (mon intention était bien d'écrire du PHP dans pass.php :p (au cas où))


Ceci dit, dans le .zip c'est bon, c'est surement ton moteur de colorisation syntaxique qui s'amuse x)


Par contre j'avais complètement zappé le deuxième argument de var_export, pardon x)


J'étais (et suis encore comparé à certains :p) plutôt noob niveau sécurité également mais j'ai laissé trop longtemps mon site avec une sécurité "faible", donc quitte à devoir revoir sa sécurité, autant le faire du mieux possible !


Là, je suis entrain de me dev une lib pour lutter contre les attaques CSRF (le rejeux d'URL) via la création jetons uniques (token) … J'aimerais bien share après mais j'ai un peu du mal à faire un KISS '


HS: Dur, le captcha me demande la 5ème lettre du mot :(

 
9  Bronco le

Faire un backlink me paraît tout à fait normal ;)
Curieux pour le <?-- -->, je ne l'ai pas, du coup je pense qu'en effet, c'est la coloration syntaxique qui fait des siennes...


C'est vrai que la sécurisation est une étape à part entière d'un projet qu'on a tendance à négliger ;)


Je serai curieux de jeter un oeil sur ta lib lorsque tu l'auras terminée ...


(vertuchou, voilà que le capcha me fait le même gag... )

 
10  JeromeJ le

Raah non, je vais devoir (essayer de) coder "propre" du premier coup alors :o


Pas que je code mal, mais ça m'est déjà arrivé (souvent …) de laisser des vieux bouts de codes moisis avant d'enfin les retaper quand un jour par hasard je les recroise "OH MON DIEU, c'est moi qui ai écrit ça !?"


vertuchou x) c'est même pas un mot qui existe pas, OMG :D ne fais pas ma culture française !
Anthropomorphisme, manichéen, syllogomane, procrastination … euh cherche des mots "compliqués" (tous le sont pas mais il est parfois étonnant de constater que certains mots sont enfaite peu connus )

 
11  Bronco le

On en est tous là: un truc qu'on a codé à la va vite puis oublié... j'en frissonne encore rétrospectivement


J'ai plein de jurons médiévaux comme celui-là: c'est comme le point-virgule, il faut les utiliser pour les sauver
En plus, c'est la classe quand tu balances un vertuchou en lieu et place d'un 'bordel' tonitruant... ;)

 
12  Pas de pub commerciale ^^ le

Your article is great, thanks for sharing.

 
13  JeromeJ le

Epic spam is epic :) au moins ils sont pas méchants :p (ils en ont à gagner je crois)


Je te reconseille vivement honeypot même si je ne l'ai pas encore vraiment tester personnellement (moins nécessaire puisque j'en suis à la restriction "être membre pour poster") tout ceux l'utilisant en font de grandes éloges :)


J'avais pas vu la nouvelle image d'auto_restrict, je remercie donc ce bot spammeur de m'avoir amené ici :) elle est chouette cette image.

 
14  Bronco le

@Pas de pub commerciale :
You're welcome, thank you ! this is your email for the spambots alexnew_8197@aol.com

 
15  Bronco le

@JeromeJ :
je vais regarder ça de plus près Merci !

 
16  four seasons hotel dublin le

Salut, Certes ce thème s'avère être moins simple qu'on ne pourrait le penser, et votre manière de l'appréhender permet une meilleure compréhension. En tout cas, je repasserai certainement plus tard Vous avez la possibilité de venir visiter ma page Internet si vous le voulez. Au plaisir et longue vie à ce blog !
natasha88@mega.zik.dj

 
17  JeromeJ le

Bien sûr

 
18  Bronco le

@four seasons hotel dublin :
Quelle émotion m'étreint à la lecture de cet éloge vibrant et totalement désintéressé...

 
19  Bronco le

@four seasons hotel dublin :
Quelle émotion m'étreint à la lecture de cet éloge vibrant et totalement désintéressé...

 

Fil RSS des commentaires de cet article

✍ Écrire un commentaire

les commentaires relevant du SPAM seront filtrés et dégagés direct...

Quelle est le premier caractère du mot n47stqv ?