Discussion: Les bonnes habitudes

  1. #1
    Membre habitué
    Homme Profil pro
    Sysadmin Linux
    Inscrit en
    mars 2017
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Sysadmin Linux

    Informations forums :
    Inscription : mars 2017
    Messages : 76
    Points : 191
    Points
    191

    Par défaut Les bonnes habitudes

    Compiler les sources de PHP



    1) Pourquoi compiler depuis les sources PHP a la place des paquets pré-compile ?

    La première chose a analyser, lors d’une installation de PHP, serait de vous demander si*l’installation va servir pour une utilisation générale (entreprise, publique, quotidiennement) ou n’a pour but que d’exécuter du PHP de temps a autre (comme des testes en local, sur votre machine). Pour une utilisation local, il n’y a généralement pas ou peu d’intérêts.

    PHP lorsqu’il est installé par le moyen de paquet pré-compilé ,va inclure beaucoup de modules dont l’utilisation n’est pas requis par votre projet. Votre installation PHP sera par conséquent moins performant, moins optimisé et moins sécurisé.

    A chaque nouvelle mise a jour de PHP, vous pouvez trouver les instructions de compilation et d’install pour Linux sur linuxfromscratch.com :

    Si lors de l’étape, ./configure ne renseigne pas les modules activé par défaut a ne pas installer, lors de l’étape de compilation, ruinant même l’intérêt de compiler les sources. Utilisez ./configure –help pour lister l’intégralité des modules

    http://dk2.php.net/manual/fr/configure.about.php

    Prenez du temps a créer votre .*/configure bien spécifique, vous pourrez réutiliser cette commande autant de fois qu’une mise a jour de PHP est disponible et facilement ajouter des modules désiré. Par exemple si vous ne désirez pas du modules FTP, Calendar et FTP (par défaut ces 2 modules sont présent), mais désirez le support mySQL, comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ./configure –disable-calendar –disable-ftp -with-mysqli=mysqlnd -with-mysql-sock=/var/run/mysqld/mysqld.sock --with-pdo-mysql
    Plus vous limitez les modules de PHP, plus il sera rapide, moins gourmand et plus sécurisé. Le reste de la procédure d’installation est disponible sur LinuxFromScratch :

    http://www.linuxfromscratch.org/blfs...neral/php.html




    MySQL, Utilisez les requêtes préparées



    1) Pourquoi utiliser les requêtes préparées ?

    La raison est simple : contrairement aux requêtes directe qui peuvent instruire a mySQL, par le biais de certains caractères spéciaux, des commandes ou des ajout de d’instructions. Le problème survient quand le code est mal conçu et autorise une variable mal nettoyé/vérifié aux caractères spéciaux (comme les guillemets, simple et double) a modifier arbitrairement la requête SQL.

    Les requêtes préparée sont différentes car vous choisissez le type de variable que vous soumettez, dés lors, même si des caractères spéciaux venait a être exécuté, mySQL les traitera comme un caractère normal. Les injections SQL ne sont donc plus possible.

    Dans beaucoup de conception en PHP utilisant mySQL, les requête direct (mysqli_query) n’ont pas ou peu d’intérêt et devraient être remplacé par la méthode préparée pour éviter l’injection de SQL, car même si vous développez de maniéré sécurisé, l’erreur est humaine.

    2) Se connecter au serveur de base de données avec les requêtes préparées

    Il est recommande de suivre ce schéma de conception pour vos projets :

    - Créer un nouveau dossier a la racine de votre dossier web
    - nommez le (include ou bdd ou db…)
    - Créer un nouveau fichier php : db.php

    Insérez y le code suivant, en remplaçant les informations de votre base de donnée

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    <?php
     
    /* 
    Créer une nouvelle connexion
    Spécifier entre les guillemets les informations de votre BDD:
     
    «localhost»: le serveur a utiliser, peut-être une IP ou un domaine 
    «utilisateur»: l’utilisateur de la BDD
    «mot de passe»: le mot de passe de votre BDD
    «bdd»: le nom de la base de donnée
    */
     
    $mysqli=new mysqli("localhost","utilisateur","mot de passe","bdd");
     
    //Si la connexion a échouée
    if*($mysqli->connect_errno) {
    echo "Echec lors de la connexion à MySQL : (".$mysqli->connect_errno.")".$mysqli->connect_error;
    }
     
    ?>
    3) Communiquer avec mySQL via les requêtes préparées en PHP

    Créer un nouveau fichier a la racine de votre site, nommez le teste.php et insérez y le contenu suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    <?php
     
    //Inclure le fichier de connexion a mySQL
    require(__DIR__ . '/include/db.php');
     
    //Configuration de la requête SQL, ici SELECT
    $stmt = $mysqli->prepare("SELECT id, label FROM teste");
     
    //Exécution de la requête SQL
    $stmt->execute();
     
    //Récupéreration des valeur du champ id et de label, dans un tableau
    $stmt->bind_result($id, $label);
     
    //Il faut passer en revu toutes les entrées que le tableau puisse avoir récupérer dans notre table mySQL
    while ($stmt->fetch()) {
        //Pour chaque entrée du tableau, nous affichons le résultat 
        echo "$id $label \n\r";
     
    }
     
    ///!\ Optionnellement /!\
    //On libère les résultats de la mémoire 
    $stmt->close();
     
    ///!\ Optionnellement /!\
    //Fermer la connexion a mySQL
    $mysqli->close();
     
    ?>
    Pour passer une variable PHP dans la requête préparée*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
    <?php
     
    //Inclure le fichier de connexion a mySQL
    require(__DIR__ . '/include/db.php');
     
    //Variable
    $section = "home";
     
    //Configuration de la requête SQL, ajoutez ? par chaque variable que vous desirez passer
    $stmt = $mysqli->prepare("SELECT id, label FROM teste WHERE section = ?");
     
    //Définir le type de variable 
    # - Si section a un valeur numérique, renseigner le drapeau d (décimal)
    # - Si section a pour valeur du texte, renseigner le drapeau s (chaîne de caractères) 
    # - Si Ici la variable utilisateur sera transmise a la place du ? de la requête préparée 
     
    $stmt->bind_param("s", $section);
     
    //Exécution de la requête SQL
    $stmt->execute();
     
    //Récupération des valeur du champ id et de label, dans un tableau
    $stmt->bind_result($id, $label);
     
    //Il faut passer en revu toutes les entrées que le tableau puisse avoir récupérer dans notre table mySQL
    while ($stmt->fetch()) {
        //Pour chaque entrée du tableau, nous affichons le résultat 
        echo "$id $label \n\r";
     
    }
     
    ///!\ Optionnellement /!\
    //On libère les résultats de la mémoire 
    $stmt->close();
     
    ///!\ Optionnellement /!\
    //Fermer la connexion a mySQL
    $mysqli->close();
     
    ?>
    Pour passer plusieurs variables*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
    <?php
     
    //Inclure le fichier de connexion a mySQL
    require(__DIR__ . '/include/db.php');
     
    //Variable strings (texte = s)
    $section = "home";
    //Variable decimal (= d)
    $id_test = 5;
     
    //Configuration de la requête SQL, ajoutez ? par chaque variable que vous désirez passer
    $stmt = $mysqli->prepare("SELECT label FROM teste WHERE section = ? AND id = ?");
     
    // Définir le type de variable Les variables remplaceront les ?, dans l'ordre donné 
    $stmt->bind_param("sd", $section, $id_test);
     
    //Exécution de la requête SQL
    $stmt->execute();
     
    //Récupération des valeur du champ id et de label, dans un tableau
    $stmt->bind_result($label);
     
    //Il faut passer en revu toutes les entrées que le tableau puisse avoir récupérer dans notre table mySQL
    while ($stmt->fetch()) {
        //Pour chaque entrée du tableau, nous affichons le résultat
        echo "$label \n\r";
     
    }
    ///!\ Optionnellement /!\
    //On libère les résultats de la mémoire
    $stmt->close();
     
    ///!\ Optionnellement /!\
    //Fermer la connexion a mySQL
    $mysqli->close();
     
    ?>
    Si votre variable est un $_POST ou un $_GET*:


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    <?php
    //Inclure le fichier de connexion a mySQL
    require(__DIR__ . '/include/db.php');
     
    //Variable $_GET
    $section = $_GET['section'];
     
    //Configuration de la requête SQL, ajoutez ? par chaque variable que vous désirez passer
    $stmt = $mysqli->prepare("SELECT label FROM teste WHERE section = ?");
     
    // Définir le type de variable Pas besoin de vérifier la variable ou l'encapsule dans une fonction pour échapper les caractères spéciaux
    $stmt->bind_param("s", $section);
     
    //Exécution de la requête SQL
    $stmt->execute();
     
    //Récupération des valeur du champ id et de label, dans un tableau
    $stmt->bind_result($label);
     
    //Il faut passer en revu toutes les entrées que le tableau puisse avoir récupérer dans notre table mySQL
    while ($stmt->fetch()) {
        //Pour chaque entrée du tableau, nous affichons le résultat
        echo "$label \n\r";
     
    }
     
    /!\ Optionnellement /!\
    //On libère les résultats de la mémoire
    $stmt->close();
     
    /!\ Optionnellement /!\
    //Fermer la connexion a mySQL
    $mysqli->close();
     
    ?>
    Ce schéma est ainsi le même pour les autres types de requêtes*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    <?php
     
    //Inclure le fichier de connexion a mySQL
    require(__DIR__ . '/include/db.php');
     
    //Variable $utilisateur
    $username = $_POST["username"];
    $id = 6;
     
    //Configuration de la requête SQL, ajoutez ? par chaque variable que vous désirez passer
    $stmt = $mysqli->prepare("UPDATE teste SET user = ? WHERE id = ?");
     
    /* Définir le type de variable */
    $stmt->bind_param("sd", $username, $id);
     
    //Exécution de la requête SQL
    $stmt->execute();
     
    //Vérifier que la requête UPDATE a bien fonctionné
    if($stmt->affected_rows === 0)
        echo "Erreur";
    else
        echo "Mis a jour avec succès";
     
    /!\ Optionnellement /!\
    //On libère les résultats de la mémoire
    $stmt->close();
     
    /!\ Optionnellement /!\
    //Fermer la connexion a mySQL
    $mysqli->close();
     
    ?>
    Pour la liste des autres fonction, rendez-vous sur

    http://php.net/manual/fr/class.mysqli-stmt.php




    Les traverser

    1) Qu-est-ce que sont les traversées de répertoire ?

    Les traversées de répertoire est une technique permettant d’exploiter les fonctions se servant des chemin d’accès du système ou des chemins externes (exemple url). Elle fait parti du top 10 OWASP

    De nombreuse fonction PHP utilise les chemins d’accès :

    - file_get_contents
    - file_put_contents
    - require
    - include

    Si le code est mal conçu, il est possible de changer le chemin voulu par un autre, devenant ainsi une vulnérabilités exploitable. Les serveur web mal configuré (pas de chroot, lance en root…) peuvent même permettre a l’attaquant de révéler des fichiers sensibles en dehors du répertoire racine web.
    Il n’existe pas de procédé unique pour éviter ces vulnérabilités, selon le fonctionnement de votre code, vous devez être sur que le chemin d’accès utilise pour la fonction PHP est correct.

    Quelques exemples (basé sur Linux) de code vulnérable pour vous aider dans la compréhension

    Exemple 1 :

    Utiliser l’url pour inclure du contenu, par exemple lorsque qu’un client clique sur lien menu html

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    <?php
     
    //La valeur est /contact.php pour le site https://votresite.com
    //Le chemin racine web est : /var/www/
    $uri = $_SERVER['REQUEST_URI'];
     
    //Include page/contact.php
    include("page$uri");
     
    ?>
    Le code ci-dessus est exploitable, le client peut changer l’url pour y inclure un chemin, remonter et afficher des données sensible sur votre système.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    https://votresite.com/../../etc/passwd
    Pour sécuriser cette exemple, $uri pourrait être vérifier a l’aide d’un array contenant les pages php disponible.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
     
    <?php
     
    //La valeur est /../../etc/passwd pour le site https://votresite.com
    //Le chemin racine web est*: /var/www/
    $uri = $_SERVER['REQUEST_URI'];
     
    //Page disponible
    $page = ['/contact.php','/blog.php','/about.php','/homepage.php'];
     
    //Exit si l'url est incorrect
    if(!in_array($uri, $page))
        exit('Erreur, la page n\'existe page.');
     
    //Include page apres verification
    include("page$uri");
     
    ?>
    Exemple 2 : Récupérer un fichier

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     
    <?php
     
    //La valeur est /homepage.php?file=rapport.pdf pour le site https://votresite.com
    //Le chemin racine web est*: /var/www/
    $pdf = $_GET['file'];
     
    //Vérifier que le PDF existe
    if(!file_exists(__DIR__.'/files/'.$_GET['file']))
        exit('Pas de PDF sous ce nom');
     
    file_get_contents(__DIR__.’/files/'.$_GET['file']);
     
    ?>
    Dans cet exemple, créer un tableau avec tous les noms de fichier pdf est impossible, car il y en a plus de 5000.

    Le code comporte une condition qui vérifie si, le pdf demandé existe. Cet exemple est incorrect car il est possible de contourner cette vérification et d’exploiter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    https://votresite.com/homepage.php?file=rapport.pdf%00woot
    %00 est un nullbyte et peut empêcher une vérification des caractères se positionnant après le nullbyte. Bien que nombre de code ne seront pas forcement affecté directement par une traversée, un simple bug que l'application pourrait afficher est un problème a corriger.

    Pour sécuriser la vérification, il faut d’insérer l’extension en dur

    (le code ci dessous n'est qu'un exemple de compréhension, évitez ce genre de code en production)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    <?php
     
    //La valeur est /homepage.php?file=rapport.pdf pour le site https://votresite.com
    //Le chemin racine web est*: /var/www/
    $pdf = substr($_GET['file'], 0, -4);
     
    //Vérifier que le PDF existe
    if(!file_exists(__DIR__.'/files/'.$_GET['file']).'.pdf')
        exit('Pas de PDF sous ce nom');
     
    file_get_contents(__DIR__.'/files/'.$_GET['file'].'.pdf');
     
    ?>
    Le moyen le plus efficace serait de réduire les possibilités de nom possible comprenant seulement des caractères alphanumérique

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    <?php
     
    //La valeur est /homepage.php?file=rapport pour le site https://votresite.com
    //Le chemin racine web est*: /var/www/
    $pdf = $_GET['file'];
     
    //Vérifier que les caractères de $pdf sont alphanumérique
    if(!preg_match('^([a-zA-Z0-9]+)$', $pdf))
        exit('Doit contenir que des caractères alphanumérique');
     
    //Vérifier que le PDF existe
    if(!file_exists(__DIR__.'/files/'.$_GET['file']).'.pdf')
        exit('Pas de PDF sous ce nom');
     
    //Récupéreration du fichier
    file_get_contents(__DIR__.'/files/'.$_GET['file'].'.pdf');
     
    ?>
    Penser lors de l’utilisation de chemin d’accès en PHP, de toujours limiter les caractères de vos variables (celles qui seront utilise comme chemin d’accès), de vérifier que le chemin d’accès est correcte et qu’aucune possibilité de changement puisse être exploité

  2. #2
    Modérateur
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    mars 2004
    Messages
    4 251
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : mars 2004
    Messages : 4 251
    Points : 10 474
    Points
    10 474

    Par défaut

    Salut,

    quelques précisions :
    préparer tout n'est pas forcément une excellente idée dans la mesure où cette étape nécessite du temps de traitement.
    Par exemple, sur ce genre de requête : $stmt = $mysqli->prepare("SELECT id, label FROM teste"); tu peux sauter la préparation.

    Ensuite, ne te préoccupe pas de la libération des ressources, PHP le fait pour toi et de manière bien plus véloce que $stmt->close(); ou encore $mysqli->close();.
    N'oublie pas que PHP est "stateless" et donc il ferme tout seul en toute fin de traitement ce qui a été utilisé durant l'exécution (y compris les ressources).

    Après je conseillerai très fortement à tous de s'orienter plutôt vers la découverte de la bibliothèque PDO au lieu de mysqli.
    Ne serait-ce que pour une seule fonctionnalité : PDO offre le binding par valeur alors qu'il est clairement dit dans la doc que :
    Notez que mysqli_stmt_bind_param() nécessite que ses paramètres soient passés par référence
    La gestion des références peut devenir un véritable cauchemar dans les gros traitements.

    Merci pour l'effort de présentation et d'explication
    # Dans la Création, tout est permis mais tout n'est pas utile...

  3. #3
    Membre habitué
    Homme Profil pro
    Sysadmin Linux
    Inscrit en
    mars 2017
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Sysadmin Linux

    Informations forums :
    Inscription : mars 2017
    Messages : 76
    Points : 191
    Points
    191

    Par défaut

    Citation Envoyé par rawsrc Voir le message
    Salut,

    quelques précisions :
    préparer tout n'est pas forcément une excellente idée dans la mesure où cette étape nécessite du temps de traitement.
    Par exemple, sur ce genre de requête : $stmt = $mysqli->prepare("SELECT id, label FROM teste"); tu peux sauter la préparation.
    Effectivement, vu que la requête ne contient pas de saisi client / inconnu, utiliser query de mysqli directement ne pose pas de problème de sécurité direct. Cet exemple est surtout pour faire comprendre comment fonctionne les requêtes préparés

    Citation Envoyé par rawsrc Voir le message
    Ensuite, ne te préoccupe pas de la libération des ressources, PHP le fait pour toi et de manière bien plus véloce que $stmt->close(); ou encore $mysqli->close();.
    N'oublie pas que PHP est "stateless" et donc il ferme tout seul en toute fin de traitement ce qui a été utilisé durant l'exécution (y compris les ressources).
    Encore une fois, c’est exact. J’essaye de baser ces exemples au mieux de ce qui est explique sur php.net. stmt close et mysqli_close peuvent sont donc optionnels j’éditerais la présentation dans la journée

    Merci pour ces informations

    Edit : Correction et ajout de Compiler Php / Les traversé

Discussions similaires

  1. [PHP] librairie gérant les XSL
    Par Mayhem dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 25/01/2006, 15h16
  2. [PHP] PB sur les formulaires
    Par chaser_T dans le forum Formulaires
    Réponses: 6
    Dernier message: 10/01/2006, 06h35
  3. ms sql server et php : problème avec les caractères accentués
    Par stephane9422 dans le forum PHP & SQL-Server
    Réponses: 6
    Dernier message: 05/12/2005, 17h45
  4. php:comment utiliser les sessions en php
    Par feten dans le forum Sessions
    Réponses: 3
    Dernier message: 19/11/2005, 16h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo