S�curisation des op�rations by LauraLimem

VIEWS: 8 PAGES: 4

More Info
									Sécurisation des opérations (formulaires)
   2.1. Avant propos
Toutes les opérations effectuées à partir de formulaires sont soumises à de nombreux risques car les données
qu'elles traitent proviennent de l'extérieur et sont donc non sûres.


Il convient donc de vérifier que les types des données reçues sont bien du type attendu et de protéger toutes les
données textes des caractères qui pourront être interprétés par le parseur de la base de données et conduire à des
injections SQL. Les manières de contrôler et protéger vos données sont abordés dans cet article


Cependant, vérifier les données ne suffit pas ! En effet, toutes vos opérations partent du principe que votre
application fait confiance à l'utilisateur et que toutes les actions qu'il initie sont de son propre fait.
C'est à partir de ce principe qu'un hacker va pouvoir abuser de cette confiance et automatiser certaines taches voir
faire exécuter des tâches à un utilisateur à son insu via une attaque de Cross-Site Request Forgeries.


   2.3. Cryptogramme visuel
Le principe du cryptogramme visuel est de présenter à l'utilisateur une image contenant un code qu'il devra recopier
afin de valider l'exécution de l'action demandée.
Cette méthode est surtout utilisée pour protéger les formulaires d'inscription, mais elle peut aussi servir pour les
formulaires d'identification ou de changement de mot de passe.


Le principe de fonctionnement est simple, on génère un code, que l'on affiche dans une image et sauvegarde dans la
session, lors de la validation du formulaire, on vérifie que le code entré par l'utilisateur est le même que celui stocké
dans la session.


Pour cela nous allons utiliser la classe suivante :


class VisualCryptogram
{
    /*
     *    Méthode statique publique generate
     *    Génère un code aléatoire et renvoit une image
     *    Aucune valeur de retour
     */
    public static function generate() {
        // Active la session PHP si ce n'est pas déjà fait
        if(session_id() == "")session_start();

            /*
             *       Entêtes de la réponse HTTP
             *       Spécifie qu'il s'agit d'une image et qu'elle ne doit pas être mise e
n cache
             */
            header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
            header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
            header("Cache-Control: no-store, no-cache, must-revalidate");
            header("Cache-Control: post-check=0, pre-check=0", false);
            header("Pragma: no-cache");
            header("Content-type: image/png");

        // Génération du code qui sera affiché (Les caractères trop semblables son
t supprimés)
        $charset = "23456789ABCDEFGHJKMNPQRSTUVWXYZ23456789";
        $secucode = "";
        for($i = 0 ; $i < 8 ; $i++) $secucode .= $charset[mt_rand(0,strlen($charse
t)-1)];

            // Sauvegarde du code dans la session
            $_SESSION['visualcryptogram'] = $secucode;

            // Listing des polices utilisées pour l'affichage du code
            $fonts = array("fanta___.ttf", "wobbly.ttf","BUNGNIPP.TTF");
           // Création de l'image
           $img    = imagecreate(200, 100);
           $white    = imagecolorallocate($img , 255, 255, 255);
           $black    = imagecolorallocate($img , 0, 0, 0);

        // Ecriture des lettres
        for($i=0 ; $i < strlen($secucode) ; $i++) {
            // Choix d'une police au hasard
            $font = mt_rand(0, count($fonts)-1);
            // Choix d'un angle au hasard
            $angle = mt_rand(-13 , 13);
            // Ecriture de la lettre
            imagettftext($img, 20, $angle, 5 + (34 * $i), 40, $black, $fonts[$font
], $secucode[$i]);
        }

        // Renvoit de l'image
        // Le format jpeg permet d'avoir une image de qualité dégradée et donc d'a
utant plus dur à reconnaître par OCR
        imagejpeg($img);
        // Suppression de l'image en mémoire
        imagedestroy($img);
    }

    /*
     *    Méthode statique publique check()
     *    Vérifie que le code passé en paramètre correspond bien au cryptogramme v
isuel
     */
    public static function check($code) {
        // Active la session PHP si ce n'est pas déjà fait
        if(session_id() == "")session_start();

           if(!isset($_SESSION['visualcryptogram'])) return false;
           return $code == $_SESSION['visualcryptogram'];
     }
}

Nous pouvons donc afficher l'image avec une page contenant simplement le code suivant :


VisualCryptogram::generate();

Et nous pouvons contrôler si le code tapé est correct de cette manière :


if(VisualCryptogram::check($_POST['cryptogram_code']) {
   // Code si OK
} else {
   // Code si ERR
}


    2.4. Système de jetons
Le système précédent permet d'empêcher les attaques de type Cross-Site Request Forgeries puisqu'elle nécessite
que l'utilisateur valide lui même l'opération.
Cependant cette méthode est contraignante pour l'utilisateur et ne peut être utilisée pour confirmer toutes les
opérations, en particulier celles effectuées dans les applications Web 2.0 qui sont pour la pluspart executées via
AJAX sans que l'utilisateur ne s'en rende compte.


Il faut donc trouver un système permettant d'être sûr que les opérations demandées par l'utilisateurs interviennent
dans un contexte normal et non dans le cadre d'une attaque.


Pour cela, nous allons mettre en place un système de jeton qui permettra de n'exécuter les opérations que si la
demande est accompagnée d'un jeton valide.
Le principe est simple, prenons l'exemple d'une suppression d'élément. Dans la page de confirmation de
suppression, nous allons créer un jeton utilisable qu'une seule fois qui devra être transmis lors de la demande de
suppression définitive afin de valider son authenticité.
Prenons un deuxième exemple, nous avons une page permettant de déplacer des éléments d'un liste et enregistrons
à chaque changement les modifications, il va falloir créer un jeton au chargement de cette page qui aura une durée
de vie limitée; renouvelée à chaque utilisation qui devra être transmis lors de toutes les sauvegardes de
modifications.


Nous allons utiliser les classes suivantes pour gérer nos jetons :


/*
  * Class Tokens
  * Fournit des méthodes statiques pour gérer les jetons de la sessions
  */
class Tokens
{
     /*
      *    Méthode statique publique addToken()
      *    Sauvegarde un jeton dans la session en l'indexant par son code et une cl
ef facultative
      */
     public static function addToken($token, $key = "") {
         if(!isset($_SESSION['token_list'])) $_SESSION['tokens_list'] = array();

           $tokencode = Tokens::generateTokenCode();
           $_SESSION['token_list'][$key.'_'.$tokencode] = $token;
           return $tokencode;
     }

    /*
     *    Méthode statique publique getToken()
     *    Renvoit un jeton en fonction d'un code et d'une clef ou null si aucun je
ton ne correspond
     */
    public static function getToken($tokencode, $key = "") {
        return isset($_SESSION['token_list'][$key.'_'.$tokencode]) ? $_SESSION['to
ken_list'][$key.'_'.$tokencode] : null;
    }

     /*
      *    Méthode statique privée generateTokenCode()
      *    Renvoit un code unique pour identifier le jeton
      */
     private static function generateTokenCode() {
         // Ce couplage de fonction permet de générer un jeton quasi unique
         // Cependant ce code est plus simple que celui préconisé par la RFC 4122
         return uniqid(mt_rand(), true);
     }
}

/*
  *    Interface Token
  *    Définit les méthodes que doivent implémenter les types de jetons
  */
interface TokenI
{
     public function __construct($restriction);
     public function isValid();
     public function consume();
}

/*
  *    Classe UsageToken
  *    Jeton limité en terme d'usage
  */
class UsageToken implements TokenI
{
     private $nbmax;
     private $nb;

     public function __construct($nb_use) {
           $this->nb = 0;
           $this->nbmax = $nb_use;
     }

     public function isValid() {
         return $this->nb < $this->nbmax;
     }

     public function consume() {
         $this->nb++;
     }
}

/*
  *    Classe UsageToken
  *    Jeton limité en terme de temps
  */
class TimeToken implements TokenI
{
     private $validity;
     private $lastconsumption;

     public function __construct($validity_time) {
         $this->validity = $validity_time;
         $this->lastconsumption = time();
     }

     public function isValid() {
         return (time() - $this->lastconsumption > $this->validity);
     }

     public function consume() {
         $this->lastconsumption = time();
     }
}

Ainsi, dans votre page qui affiche les formulaire a sécuriser, vous pourrez mettre :


$tokencode = Tokens::addToken(new UsageToken(1), 'votre_clef_interne');
echo '<input type="hidden" name="token" value="'.$token.'" />';

La clef interne vous permet de restreindre le champ d'application des jetons afin que tous vos jetons n'interfèrent
pas entre eux et ne puissent servir que pour l'opération pour laquelle ils ont été créés. Ainsi, vous pouvez spécifier la
clef 'del_elmt_4' pour votre jeton et vous saurez que ce jeton n'est valide que pour la supression de l'élément d'id 4.


Enfin, vous pourrez tester la validité d'un jeton de la manière suivante :


$token = Tokens::getToken($_POST['tokencode'], 'del_elmt_'.$_POST['id']);
if(!is_null($token) && $token->isValid()) {
    $token->consume();
    // Code si OK
} else {
    // Code si ERR
}



                                 2. Sécurisation des opérations (formulaires)




                                            Articles de la même catégorie

								
To top