197 visites

Exemples de fonctions PHP

mercredi 27 juillet 2016

Version imprimable de cet article Version imprimable

Fonctions sur les mots de passe

Diverses fonctions pour gérer l’authentification d’utilisateurs et le stockage sécurisé des mots de passe dans la BDD.

Vérification de la complexité d’un mot de passe

si la fonction renvoie une chaîne vide lorsqu’on l’appelle, tout baigne.

function chk_complex_mp($motpass){
        $message="";
        $patterns[0]="/^.*[A-Z].*+$/";        $complexe[0]="Il faut au moins une lettre majuscule<br/>";
        $patterns[1]="/^.*[a-z].*+$/";        $complexe[1]="Il faut au moins une lettre minuscule<br/>";
        $patterns[2]="/^.*[0-9].*+$/";        $complexe[2]="Il faut au moins un chiffre<br/>";
        $patterns[3]="/^.{8,}$/";                $complexe[3]="Il faut au moins 8 caractères<br/>";
        for ($i=0;$i<sizeof($patterns);$i++){
                $pattern=$patterns[$i];
                if (!preg_match("$pattern",$motpass)){
                        $message.=$complexe[$i];
                }
        }
        return $message;
}

Hashage de mot de passe avec sel, par algorithme choisi

Cette fonction n’est qu’un exemple de fonction de cryptage, il est possible de faire plus souple/plus complexe.
A partir d’un mot de passe en clair, d’une chaîne de salage et d’un algorithme de hashage, elle :

  • casse le sel en 3 parties de 10 caractères
  • casse le mot de passe en 3 parties égales
  • hash une première fois les morceaux de mot de passe mélangés aux morceaux de sel
  • re-casse le mot de passe salé et hashé en 3 parties égales
  • et enfin re-mélange le sel avec le mot de passe hashé.

function genere_motpasse($motpasse,$alt,$algo){
        $alt1=substr($alt, 0, 10);
        $alt2=substr($alt, 10, 10);
        $alt3=substr($alt, 20, 10);
        $taillepass=strlen($motpasse);
        $taillemorceau=intval($taillepass/3);
        $pass1=substr($motpasse,0,$taillemorceau);
        $pass2=substr($motpasse,$taillemorceau,$taillemorceau);
        $pass3=substr($motpasse,($taillemorceau*2));
        $passfin=hash($algo,"$pass1$alt1$pass2$alt2$pass3$alt3");
        $taillepassf=strlen($passfin);
        $taillemorceauf=intval($taillepassf/3);
        $passf1=substr($passfin,0,$taillemorceauf);
        $passf2=substr($passfin,$taillemorceauf,$taillemorceauf);
        $passf3=substr($passfin,($taillemorceauf*2));
        $passfou="$passf1$alt1$passf2$alt2$passf3$alt3";
        // le mot de passe coupé, salé, hashé et re-salé est :       
        return $passfou;
}

Génération de mot de passe aléatoire et/ou de sel

voir l’article sur le salage en informatique sur Wikipedia.

Cette fonction génère donc une chaîne aléatoire, par exemple pour le sel aléatoire, qui devra être stocké, éventuellement pour chaque utilisateur.
Cela permet de se protéger (dans une certaine mesure) des attaques par rainbow table.

// génère une chaîne alpha-numérique aléatoire de longueur $taille
function genere_chaine_aleatoire($taille) {
        $code_aleatoire="";
        $chaine="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        srand((double)microtime()*1000000);
        for($i=0; $i<$taille; $i++) {
                $code_aleatoire.=$chaine[rand()%strlen($chaine)];
                // ou $code_aleatoire .= substr($chaine,rand()%(strlen($chaine)),1);
                // ou $code_aleatoire .= $chaine[ rand(0, ($nb_chars-1)) ];
        }
        // ou $code_aleatoire = str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789');
        return $code_aleatoire;
}

si l’on veut casser et mélanger le sel et le mot de passe avant/après cryptage selon les chiffres compris dans le sel, il est possible, à sa génération, de rajouter une chaîne "0 à 9" au sel si celui-ci ne contient pas de chiffre, puis de mélanger le sel par la fonction str_shuffle().

Choix d’un algorithme de hashage

Ce bout de code cherche dans le tableau renvoyé par la fonction "hash_algos" les meilleurs algorithmes de hashage.
hash_algos() renvoie les algorithmes supportés sur le système.

Une fois trouvé, l’algorithme à utiliser pourra être stocké soit dans le fichier connect.php, soit dans la base de données, dans une table de méta-variables.

                // chercher dans hash_algos si sha512, sha256, sha1... sont utilisables.
                $algos_prefs=array("sha512", "whirlpool", "sha384", "ripemd320", "sha256", "snefru256", "ripemd256", "snefru", "gost", "sha224", "ripemd160", "ripemd128", "fnv164", "joaat", "crc32b", "adler32", "fnv132", "crc32");        
                $algo=null;
                $i=0;
                $nbalgos=sizeof($algos_prefs);
                while (($i<$nbalgos)&&($algo==null)){
                        if (in_array($algos_prefs[$i],hash_algos())){
                                $info.="<span class=\"info\">".$algos_prefs[$i]." est supporté</span><br/>";       
                                $algo=$algos_prefs[$i];
                        }else{
                                $info.="<span class=\"debug\">".$algos_prefs[$i]." non supporté</span><br/>";
                                $i++;
                        }
                }
                if ($algo==null){
                        $info.="<span class=\"error\">Aucun algorithme supporté parmi ceux cherchés</span><br/>";
                        $debug.="Aucun algorithme supporté parmi ceux cherchés";
                }

Fonctions pour PDO

PDO est une couche d’abstraction en PHP permettant d’adresser une base de données indépendamment de son type (MySQL, PostGres, Oracle...)
Par sécurité, il est préférable de faire des requêtes paramétrées afin d’éviter l’injection SQL.
Le contrecoup, c’est le rajout de code qui devient plus long à écrire.

Le code étant toujours le même, il est toutefois possible de le simplifier en créant une fonction simple qu’il suffira d’appeler.

Cette première fonction permet de préparer et exécuter une requête, laquelle peut être paramétrée, et dont le résultat pourra être :

  • plusieurs lignes, traitées ensuite dans une boucle foreach
  • un nombre de ligne modifié

Elle effectue donc un prepare, puis bindValue pour 0-n paramètres, puis un execute, et renvoie l’objet requête PDO.
les paramètres de la requêtes sont fournis dans un tableau qui commence à 1 (de préférence) ou zéro
la requête doit donc avoir " ?" en paramètre(s) afin de pouvoir les affecter depuis un tableau par indice, plus facilement traitable. voir l’exemple plus bas

function prepexec($rek,$params,$db){
        if (isset($params[0])){
                $debut=0;
                $correc=1;
                $fin=sizeof($params)-1;
        }else{
                $debut=1;
                $correc=0;
                $fin=sizeof($params);
        }
        $rk=$db->prepare($rek);
        // si tableau param vide, pas de bindValue => fonction unique prepare avec/sans param + exec
        for ($i=$debut;$i<=$fin;$i++){
                $typ_param=PDO::PARAM_STR;// PAR DEFAUT
                $param=$params[($i+$correc)];
                if (preg_match("/[\w]+/",$param)){ $typ_param=PDO::PARAM_STR; } // PDO::PARAM_STR
                if (preg_match("/^[+-]?\d+$/",$param)){ $typ_param=PDO::PARAM_INT; } // PDO::PARAM_INT
                // PDO::PARAM_BOOL -  MySQL might not support PARAM_BOOL
                if (preg_match("/^$/",$param)){ $typ_param=PDO::PARAM_NULL; } // PDO::PARAM_NULL
                $rk->bindValue($i,$param,$typ_param);
        }
        // renvoie la requête préparée et executée
        $rk->execute();
        // $nb=$rk->execute(); OU foreach ($rk as $result){
        // donc $nb=prepexec(... ou $rk=prepexec puis foreach($rk... ou même foreach(prepexec...
        return $rk;
}

exemple d’appel :

                $rek="select count(login) as existe from utilisateur where login=?";
                $params[1]=$user;
                $rk=prepexec($rek,$params,$db);
                $existe=0;
                foreach ($rk as $result){
                        $existe=$result['existe'];
                }

Cependant, cela est encore trop long.
Cette deuxième fonction va donc faire le boulot en appelant la première et en renvoyant la valeur attendue, unique, et dont le nom du champ est indiqué.

function simplexec($rek,$params,$db,$nomvar){
        $resultat=null;       
        $rk=prepexec($rek,$params,$db);
        foreach ($rk as $result){
                        $resultat=$result["$nomvar"];
        }
        return $resultat;
}

Retour des erreurs

Lorsque les erreurs et autres messages à caractère informatif sont gérés dans les différents blocs, et empilés dans une variable qui sera au final affichée dans un bloc dédié (lequel peut être dépliant grâce au CSS).
Si l’on souhaite un style différent selon le niveau d’erreur atteint, les différents blocs peuvent faire appel à cette fonction, qui use d’opérateurs ternaires.

3 niveaux d’erreur sont gérés :

  • info : tout va bien, on affiche un (ou plus) message(s) juste pour information
  • debug : attention, risque d’erreur, tout n’est pas optimal
  • error : erreur critique

function raise_debuglvl($lvl,$actuel){
        $debuglvl=$actuel;
        $debuglvl=(($lvl=="info")&&($actuel=="info"))?"info":$debuglvl;
        $debuglvl=(($lvl=="debug")&&($actuel!="error"))?"debug":$debuglvl;       
        $debuglvl=($lvl=="error")?"error":$debuglvl;
        return $debuglvl;
}

La déclaration du bloc d’affichage :

// bloc d'information - l'image sert au déploiement mais reste affichée dans le bloc déplié
         if ($debug!=""){
                 switch ($debuglvl) {
                        case "info" : $debugimg="notif-info.gif"; break; // ou action-s-valide
                        case "debug" : $debugimg="notif-warning.gif"; break;
                        case "error" : $debugimg="notif-error.gif"; break;
                        default: $debugimg="notif-warning.gif"; break;
                }
                 echo "<div id=\"debug\" class=\"$debuglvl\">
                         <img src=\"imj/$debugimg\" alt=\"$debuglvl\"/>
                         $debug
                 </div>";
         }

La page appelante déclare les variables $debug et $info :

  • $debug contiendra les messages à afficher dans le bloc dédié
  • $info contiendra les messages à afficher dans la page.

Les pages appelées (via include ou require empileront dans ces variables les messages qu’elles souhaitent soulever.
$debug.="message"; ou $info.="message";, cette dernière variable pouvant être affichée dans la page puis vidée.

exemple d’utilisation

dans un script gérant le login d’un utilisateur, on récupère (en étape 2) login/mot de passe.
Selon ce qui est fourni, on retourne en étape 1 en affichant un message d’erreur.

if ($etape==2){
                if ((isset($_POST["user"]))&&($_POST["user"]!="")){
                        $user=$_POST["user"];
                        // vérifier pas d'espace (entre autre)
                        if (!preg_match("/^[a-z][a-z0-9]+$/",$user)){
                                $debug.="nom utilisateur non conforme<br/>";
                        }
                }else{
                        $debug.="nom utilisateur manquant<br/>";
                }
                if ((isset($_POST["motpass"]))&&($_POST["motpass"]!="")){
                        $motpass=$_POST["motpass"];
                        // vérifier pas d'espace (entre autre)
                        if (!preg_match("/^[a-zA-Z0-9]+$/",$user)){
                                $debug.="mot de passe non conforme<br/>";
                        }
                }else{
                        $debug.="mot de passe manquant<br/>";
                }
                if ($debug!=""){
                        $etape=1;
                        $info=$debug;
                        raise_debuglvl("debug",$debuglvl);
                }
}
if ($etape==2){
// comparaison BDD
}
if ($etape==1){
                echo "<div id=\"navig\" class=\"$page\">
                        Bloc navigation
                </div>
                <div id=\"contenu\" class=\"$page\">
                $info
                ";
                // formulaire
}

son style CSS

pour un bloc dépliable CSS situé en haut à gauche de la page :

#debug {
        position:absolute;
        top:0;
        left:0;
        width:32px;
        height:32px;
        z-index:42;       
        line-height:0;
        font-size:0;
}
#debug img {
        height:auto;
        width:auto;
        max-height:32px;
        max-width:32px;
        visibility:visible;
}
#debug:hover {
        height:auto;
        width:auto;
        min-height:100px;
        min-width:200px;
        line-height:normal;
        font-size:1em;
        overflow-y:auto;
}
#debug:hover img {
        height:auto;
        width:auto;
}
#debug:hover img#deploi_debug {
        height:0;
        width:0;
        visibility:hidden;
}
/* =========================================== Messages de débogages =========================================== */
#debug.error{
        background-color:#FF0000;
}
#debug.error:hover {
        background-color:#FF8C00;
}

#debug.debug {
        background-color:#FF8C00;       
}
#debug.info {
        background-color:#ADD8E6;
}
#debug.debug:hover, #debug.info:hover {
        background-color:#ADD8E6;
}
.debug {
        color:#FF8C00; /*#FFB30A;*/ /*#FFA501;*/
}
.info {
        color:#40820D;       
}
.error {
        color:#FF0000;
        font-weight:bold;
}

redirection en https

Pas vraiment une fonction, mais un petit bout de code à mettre dans la section <head>...</head>.
Si la page est accédée en http non sécurisé, on redirige vers le mode sécurisé https, via une balise meta.

                // 'HTTPS' est défini à une valeur non-vide si le script a été appelé via le protocole HTTPS.
                if ((!isset($_SERVER['HTTPS']))||($_SERVER['HTTPS']=="")){
                        $url="https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
                        echo "<meta http-equiv='Refresh' content=\"0; url=$url\"/>";       
                }

Répondre à cet article

Total 117724 visites depuis 2083 jours | Site réalisé par Vader[FR] | SPIP | | Plan du site | Suivre la vie du site RSS 2.0 | contact mail