The Butterfly Effect (2)

Antonio Fontes / Blog / Conseil / Communication / Genève / HEG / Intelligence et guerre économique / Management et sécurité de l'information / NTIC / Sécurité des applications web / Veille

<December 2008>
SuMoTuWeThFrSa
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910


Navigation

Subscriptions

Post Categories



Composants standards dans une application web

Au fur et à mesure que je développe des applications web, je constate une certaine similitude dans certains composants. Cette observation s’applique particulièrement aux composants chargés de m’assister dans le management de la sécurité globale d’une application web.

En observant l’évolution de mes propres travaux ainsi qu’en effectuant quelques recherches par ci par là, le but de cette réflexion serait de définir quels sont les composants de base dont je devrais toujours avoir besoin, une s

Pourquoi des composants ?

Il serait inutile et prétentieux de remettre en question le paradigme lié à l’utilisation de composants au sein d’une application. Particulièrement en ce qui concerne la sécurité, domaine au sein duquel une multitude de principes éprouvés s’appliquent dans tous les sens.

Un développeur choisira des composants pour les avantages suivants:

- ils seraient réutilisables;
- ils seraient modulaires;
- ils seraient utiles;
- etc (en gros, toutes les raisons que l’on voit dans les livres) …

Les méthodologies récentes, telles que XP ou RUP mettent encore plus en avant les paradigmes de simplicité (quoique… dans la pratique ce n’est plus trop ça) et d’unicité:

- ne faire que ce qui est nécessaire
- ne pas le faire plus d’une fois

Ces deux paradigmes conviennent donc particulièrement à une reflexion sur la sécurité d’une application web dans le sens où ne pas les respecter revient à accepter deux risques bien présents:

- réduire le champ de maîtrise sur l’ensemble des fonctionnalités à disposition;
- augmenter les oublis ou erreurs possibles liées à la maintenance ou amélioration de l’application si les implémentations sont présentes à plusieurs endroits dans l’architecture de sécurité;

Comment identifier les composants nécessaires ?

Il y aurait trois sources d’inspiration…

Ceux dont j’ai eu besoin dans tous les cas parce qu’ils font partie des fonctionnalités standards de chaque application web, tels que la gestion de l’authentification ou des sessions.

Ceux dont l’existence n’est pas strictement nécessaire mais le devient dans les cas où je dois répondre à des exigences de sécurité, que ce soit en interne, ou par rapport à des standards externes tels que BS7799, ISO17799 ou encore Cobit. L’on trouvera dans cette catégorie par exemple les composants liés à l’audit journalisation ainsi que la gestion de l’encryption des données.

Une troisième source d’inspiration pourrait être le Top 10 OWASP. Ce document recense les dix vulnérabilités les plus importantes constatées dans la majorité des applications web non sécurisées. Identifier ces dix failles est un pas en avant pour le management mais ce munir d’outils permettant de s’en protéger est une toute autre histoire pour laquelle bien des développeurs abandonnent l’idée de sécuriser une application web. Et je sais de quoi je parle…

Les dix failles du top ten englobent:

- la validation des entrées,
- la brêche dans les contrôle d’accès,
- la brêche dans les mécanismes d’authentification ou de gestion des sessions,
- les failles de type XSS / CSS,
- les débordements de tampons,
- les insertions de code ou de commandes,
- la gestion incorrecte des erreurs,
- une mauvaise architecture de stockage,
- les dénis de service,
- mauvaise gestion de la configuration de l’application.

Voilà donc matière à réflexion!

Après avoir identifié ces sources “d’inspiration”, pourquoi ne pas rentrer dans le détail et observer ce qui a été fait et ce qui semble cruellement faire défaut ?

1. La journalisation et l’audit

Journaliser revient à conserver une trace des événements ainsi que la date précise où ils ont eu lieu.

Auditer revient à observer si des processus soi-disant mis en place le sont réellement.

Dans mon cas, je dispose déjà d’un composant me permettant de journaliser. Par contre, si je dois auditer, je ne suis pas équipé. A moins que…un éditeur de texte doté de fonctionnalités de recherche ou un client de bases de données comprenant le langage SQL puissent être considérés comme des outils d’audit !

Mon composant d’audit a concrètement deux rôles:

- recevoir les messages décrivant les événements: les alertes (Alert());
- inscrire les alertes dans les récepteurs prévus à cet effet (Write2File, Write2DB, Write2Email, etc…);

Une alerte peut être déclenchée par un simple appel d’une méthode, imaginons alert(); , complétée de plusieurs paramètres spécifiques au point d’envoi de l’alerte. Pour ma part, je récupère trois informations:

- le projet concerné;
- le module concerné;
- le fichier concerné;
- la méthode concernée;
- le niveau d’alerte (fatal, erreur, alerte, information, bavard, debug);
- le message d’alerte;

Ces informations sont spécifiques à chaque alerte et le composant d’audit n’est pas en mesure de les identifier tout seul de manière réellement autonome pour l’instant.

En revanche, le composant d’audit complète lui-même l’alerte avec les informations complémentaires suivantes:

- l’identificateur de session;
- l’adresse IP de l’utilisateur;
- la date et heure exactes;
- l’état HTTP au moment de l’alerte (contenu de la requête HTTP)

Sa deuxième responsabilité, l’inscription du message dans les récépteurs d’alertes n’est que partiellement implémentée en ce moment: le composant sait écrire dans un fichier de type log ainsi que dans une base de données. Ce comportement est défini lors de son initialisation.

A quoi cela me sert-il ? Je peux à tout moment savoir QUI a fait QUOI, QUAND, OÙ et COMMENT avec QUELLES INFORMATIONS SPECIFIQUES au sein de l’application.

Croyez-moi, au début le fichier log avoisinnant plusieurs centaines de mégas pour un simple module de news peut à tout moment devenir un moyen de défense juridique dans les cas extrêmes!

2. La gestion des erreurs et des exceptions

Les langages de programmation récents, même s’ils sont appliqués au web adoptent de plus en plus le mécanisme de gestion des erreurs par ‘exceptions’.

Quelle différence avec les autres langages ? Rien. Si ce n’est que l’on risque de ne pas toujours voir des erreurs se produire parce que l’on a un peu trop placé d’intercepteurs d’exceptions (try…catch…etc.) là où ce n’est pas nécessaire.

Le second risque associé à la gestion des erreurs est la perte de confidentialité sur les mécanismes du fonctionnement interne de l’application. Si une requête est mal saisie et que l’identifiant de l’article est un mot au lieu d’un nombre, le serveur ne devrait JAMAIS retourner une erreur au client mais plutôt lui proposer un affichage par défaut ou de retour. A ne pas confondre avec le développeur ou l’administrateur, qui, lui, devrait pouvoir être alerté de ce genre d’événements.

Il y a donc une entente claire entre ce composant et le premier composant, chargé de gérer la journalisation: l’alerte doit être communiquée aux bonnes personnes et l’utilisateur N’EST PAS la bonne personne, contrairement à ce que l’on voit communément sur le web…

Un composant de gestion des erreurs et des exceptions aura donc deux responsabilités:

- informer uniquement les personnes concernées sur la nature de l’erreur ou exception ayant eu lieu, par la journalisation par exemple (SendErrorMessageAlert());
- rediriger l’utilisateur vers une zone encadrée et ’stabilisée’ (ErrorRedirect());

3. Le chiffrement des données

Ce composant devient nécessaire à toutes les sauces. Souvent ignoré des développeurs ne serait-ce que pour au moins stocker les mots de passes ou numéros de cartes de crédit des utilisateurs d’une application web de manière indéchiffrable au premier abord, le chiffrement de l’information arrive gentiment sur le devant de la scène.

Un composant de chiffrement devra faciliter la tâche au développeur au moins dans les trois cas suivants:

- chiffrer une chaîne de texte (EncryptString());
- déchiffrer une chaîne de texte (DecryptString());
- obtenir une empreinte d’une chaîne de texte (HashString());

Ce n’est pas monstrueux, trois méthodes au premier abord et le composant devient un ami inséparable du développeur et augmente considérablement le respect de la confidentialité des données au sein de l’application web dont il est responsable.

4. La configuration de l’application

Ce composant a pour rôle unique de pouvoir répondre à toutes les questions relatives à la configuration au sein de l’application. La mise à jour, la création ou tout simplement l’accès aux valeurs de chaque clé de configuration possible est ainsi centralisée et contrôlée par un composant unique.

Spécifiquement parlant, ce composant répond à deux cas d’utilisation:

- retourner la valeur d’une clé de configuration (GetConfigVal);
- assigner une valeur à une clé de configuration (SetConfigVal);

Cela facilite fortement la lecture du code et formalise en tous points la gestion de valeurs au sens ésotérique.

5. L’accès aux données GPSA

GPSA pour GET, POST, SESSION et APPLICATION.

Selon la technologie choisie, l’accès à ces variables peut être plus ou moins dangereux si le développeur ne prend pas soin à chaque fois de spécifier à quel ‘conteneur’ il s’adresse. Je considère ici autant les appels à des variables globales en php ou l’accès à l’objet Request utilisé en ASP par exemple.

Mon conseil: NE JAMAIS récuperer ces valeurs par les mécanismes d’accès ‘facile’ à ses variables. Si un développeur souhaite accèder à une variable de formulaire POST, il devrait utiliser les collections Request.Form de l’ASP ou la collection $_POST en PHP.

C’est facile à dire… mais tout autant facile d’oublier une fois ou l’autre de faire ce contrôle. N’oubliez pas à juste titre le principe du maillon faible:

“Le niveau de sécurité effectif d’une application est égal au niveau de sécurité en son point le moins sécurisé.”

Le composant GPSA aura donc pour rôle d’encapsuler tous les accès à l’une de ces collections en forçant le développeur à utiliser des méthodes protégées contre les cas spéciaux (valeurs nulles, vides, inexistantes). Il n’écrira non plus:

userName = Request("fUserName")

Mais plutôt:

userName = GPSA.GetFormKey("fUserName")

6. L’authentification

Le processus d’authentification, par abus de langage, regroupe deux sous-principes souvent ignorés: l’authentification et l’identification.

L’identification regroupe les mécanismes permettant à l’utilisateur d’annoncer son identité à l’application.

L’authentification regroupe les mécanismes permettant à l’utilisateur de prouver son identité à l’application.

Paradoxalement, les développeurs faisant la distinction entre ces deux processus sont les plus dangereux: c’est souvent dans le fruit de leurs efforts que l’on peut se retrouver face à un message d’erreur du genre:

Accès refusé: utilisateur inconnu

En voulant dissocier les procédures d’identification et d’authentification, le développeur fournit à un attaquant potentiel la possibilité de trouver les noms d’utilisateurs correspondants aux comptes installés sur un système d’information. C’est alors dans une seconde phase seulement que le pirate va se concentrer sur la recherche du mot de passe de l’utilisateur, la moitié du travail étant littéralement “mâchée” par le développeur.

Il est donc correct de combiner les deux processus (identification et authentification) en un seul et unique cas mais lorsque l’on sait pourquoi.

Le composant d’authentification doit répondre aux cas d’utilisation suivants:

- vérifier la validité des informations d’authentification soumises (IsUserCredentialsValid());
- détecter si un utilisateur est authentifié ou non (IsUserAuthenticated());
- stopper toute opération en cours et rediriger l’utilisateur vers un processus d’authentification (Redirect2LoginPage());

Accessoirement, il est également possible de lui conférer des responsabilités plus complexes mais néanmoins renforçant la sécurité globale de l’application:

- détruire la session de l’utilisateur (AbandonCurrentSession());
- détecter les tentatives de craquage de mots de passe et vérouiller le compte (LockAccount());
- détecter lorsqu’un utilisateur s’authentifie à partir de plusieurs endroits simultanément (IsUserAlreadyConnected());
- …

Il est bien entendu évident que ce genre de composant doit être le premier processus réel à agir lorsque la requête va être traitée par l’application. Plus la décision de refus est prise en amont, moins la confidentialité du système d’information n’est mise en danger.

7. Les permissions

Souvent confondu avec le processus d’authentification, le processus de permission reste néanmoins tout autre.

Le processus de permission vise à savoir si un utilisateur du système d’information peut ou non effectuer une certaine opération. Cette opération peut être de tout ordre: consultation, destruction, modification ou création de contenu…

Un composant de gestion des permissions bien rôdé ne répondra usuellement qu’aux cas d’utilisation suivants:

- l’utilisateur peut-il effectuer cette opération ? (IsUserAllowed());
- quel est(sont) le(s) ou niveau(x) d’accès de l’utilisateur ? (IsUserInRole());
- quel est le groupe d’appartenance de l’utilisateur ? (IsUserInGroup());

L’objectif le plus pragmatique de ce composant est de pouvoir répondre à la question:

Est-ce que [utilisateur] peut effectuer telle [opération] ? Oui / Non

Usuellement, ce composant sera positionné le plus près possible du composant chargé de gérer les authentifications des utilisateurs au niveau des flux d’informations. A nouveau, il s’agit de détecter le plus tôt possible qu’une violation des règles de sécurité est en cours et que l’utilisateur doit être immédiatement redirigé vers une zone ‘verte’.

8. La validation du contenu

La majorité des failles de sécurité résulte d’un processus de validation de contenu mal implémenté voire inexistant.

La validation de contenu regroupe les processus liés à la validation de l’information, lors de la récupération et la mise à disposition des données auxquelles le système d’informations accède.

Si un utilisateur est censé fournir une adresse de messagerie dans un formulaire, le système d’information doit pouvoir être en mesure de détecter s’il s’agit oui ou non effectivement d’une adresse de messagerie. Paralèllement, si une application doit présenter un texte à un utilisateur, il est de sa responsabilité d’afficher un texte et non un script que son navigateur ne manquera pas d’éxecuter par souci de productivité. (…)

Le premier niveau de validation de contenu se fait dans la récupération de données provenant des utilisateurs. Le second niveau est cependant atteint lorsque le contrôle est effectué dans tous les sens.

Principe de confiance fondamental en sécurité informatique:

Ne jamais faire confiance aux utilisateurs de l’application.

Dans la pratique, un composant de validation de contenu comporte usuellement deux méthodes. Une première méthode charger de tester si un contenu correspond bel et bien à un type autorisé:

userEmail = GPSA.GetFormKey("fUserEmail")
If(InputValidation.IsInputValid(userEmail, InputValidation.Patterns.Email))Then
‘* le contenu est valide
End If

Et d’une seconde méthode permettant activement de ‘nettoyer’ ou ‘préparer’ un contenu pour une utilisation future. Ce genre de composants exigera bien entendu un certain effort du développeur, particulièrement dans l’apprentissage de l’utilisation des expressions régulières.

C’est le cas par exemple de la méthode addslashes() du langage PHP:

// cette variable comporte une apostrophe et va
//déclencher une erreur SQL si cette apostrophe
//n’est pas retirée ou échappée:
$userAdress = ” Rue de la tour d’Ivoire”

//on va donc effectuer un nettoyage de la variable:
$userAdress = addslashes($userAdress);

//la requête ici sera dès lors acceptée par le SGBD:
$sql = ” UPDATE users SET adress = ‘”.$userAdress.”‘”;

En conclusion, nous avons donc les cas d’utilisation suivants:

- Le contenu est-il valide ? (IsInputValid());
- Nettoyer le contenu selon un filtre spécifique (CleanInput());

9. La récupération de valeurs

Considérons le cas suivant:

userId = “douze”
If(userId > 100)Then
‘* faire quelque chose
End If

Dans certains langages de programmation web, ce test provoquera une erreur d’éxecution. Le moteur attend en effet un nombre ou chiffre pour pouvoir effectuer son test de comparaison et la mise à disposition d’un ‘texte’ déclenche l’erreur. C’est le cas principalement des langages sans typage fort des variables.

Dans le cadre du développement web, un développeur est usuellement confronté à la récupération de variables de type:

- chaîne de texte, p. ex.: ‘un texte’;
- entier, p. ex.: ‘21';
- entier grand (ou ‘long’), p.ex.: ‘120120340304050454545023';
- nombre à virgule flottante, p.ex.: ‘120.84';

Il sera dans de très rares cas nécessaire de récupérer une valeur ne rentrant pas dans ces quatre catégories. Le composant de récupération de valeurs encapsulera donc tous les tests pouvant s’avérer nécessaires à la restitution d’une valeur dont un certain ‘type’ est attendu.

Voici un exemple de tests nécessaires pour que l’extraction d’une chaîne texte se fasse sans soucis en ASP:

Function GetString(val)

Dim retval
retval = val

If(IsEmpty(retval))Then
retval = “”
ElseIf(IsNull(retval))Then
retval = “”
Else
retval = CStr(retval)
If(retval = “undefined")Then
retval = “”
End If
End If

GetString = retval

End Function

Dans cette fonction sont dont successivement testés: la non existence de la valeur, la nullité de son contenu et finalement si elle contient du texte, que ce ne soit pas un texte automatiquement généré par le moteur ASP (undefined).

Le composant d’accès aux valeurs devra donc proposer les quatre cas d’utilisation suivants , ou plus si le développeur en a besoin:

- Récupération d’une chaîne de texte (GetString());
- Récupération d’un entier (GetInteger());
- Récupération d’un entier grand (ou ‘long’) (GetLong());
- Récupération d’un nombre à virgule flottante (GetDouble());

Conclusion

J’ai donc passé en revue neuf composants que j’estime nécessaires à un développeur souhaitant se doter d’une “boîte à outils” afin d’améliorer la sécurité de son application web.

Ces composants assument des responsabiliés vis-à-vis de certains processus directement liés à la sécurité de l’application, tels que l’authentification ou la validation de données, et peuvent dans la mesure où ils sont maîtrisés et efficacement utilisés fondamentalement élever le niveau de sécurité de leurs applications.

Bien entendu, si vous décidez de suivre cette proposition, il serait intérressant de bien vous renseigner sur l’éventuelle existance de librairies disponibles sur Internet afin de vous faire gagner du temps. Faites cependant attention à la véracité et réputation des sources de ce genre de librairies.

S’il y a des questions, n’hésitez pas!

Références:
OWASP Top 10 web applications vulnerabilities
The OWASP Guide
Improving web applications security: threats and countermeasures
The Security Patterns Repository

posted on Wednesday, September 29, 2004 1:59 PM by saphyr





Powered by Dot Net Junkies, by Telligent Systems