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 !
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:
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:
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!
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());
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:
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