Rigueur et sécurité

La nouvelle est abondamment relayée depuis sa parution. Le SANS Institute et MITRE Corporation ont publié le TOP 25 des erreurs de programmation pouvant provoquer de sérieux problèmes de sécurité. Cette liste a été élaborée conjointement entre différents organismes et sociétés impliquées dans le domaine de la sécurité informatique, et comporte les 25 erreurs les plus communément répertoriées. Ces erreurs concernent le développement web et les risques au niveau de la sécurité concernent aussi bien le côté serveur (donc l'applicatif et le fournisseur du service) que le côté client (concerne donc l'utilisateur du service). Les détails sont donnés en lien à la fin de l'article. Voyons par contre les tendances générales.

De manière globale, ces erreurs sont qualifiables en trois types : les connexions non sécurisées entre composants, la gestion risquée des ressources, et tout simplement la problématique de sécurité bâclée.

La communication entre composants

Cette catégorie regroupe toutes les insécurités liées à la communication entre composants, composants aussi bien propres à l'application (module, couches) qu'externes (appel à des programmes externes, communication avec une source de données, avec un client, un autre serveur...). La validation des données, ce grand classique et cas d'école par excellence, occupe encore la première place des trous de sécurité. Ce problème s'observe moins lorsque des frameworks sont utilisés, Struts est toujours pris en exemple (malgré des faiblesses et des mises en garde sur l'efficacité de la validation des données). Mais si la validation des formulaires est rentrée dans les habitudes, l'évolution des applications ouvre de nouvelles brèches car les données envoyées par webservices par exemple ne passeront pas par les validateurs de la couche de présentation web. Il est ainsi important d'avoir une bonne assurance de la validation des données.

Cette notion de validation et de contrôle des données est ainsi la clef de voûte de la sécurité de la communication entre composants. De part sa nature, un système d'information va, en fonction d'entrées, exécuter des tâches ou récupérer des informations. Dans le développement web ou l'informatique de gestion, la base de données occupe une place importante. Il faut donc s'assurer du contrôle sur l'échange d'informations et donc que les requêtes ne pourront pas être altérées. Les propositions de protection insistent sur le fait qu'il faut éviter de construire soi-même dynamiquement les requêtes et préconisent l'utilisation de requêtes paramétrées. Figer les requêtes évite donc une restructuration de ces dernières. Cependant, cela n'est pas toujours possible, et il convient alors de masquer la construction des requêtes ce qui peut être facilement accompli en respectant la logique des couches. L'utilisation de l'API Criteria d'Hibernate, et celle de JPA de Java EE6 permet ainsi de garantir une sécurité contre l'injection SQL tout en permettant la création dynamique de requêtes.

Il faut aussi noter qu'une base de données se paramètre et qu'il est possible de contrôler les accès et les actions par utilisateur et par table. De nombreux développements se concentrent sur une gestion de la sécurité au niveau applicatif et négligent totalement la configuration de la base de données. Votre profil "visiteur" qui n'a pas le droit de modifier les données, se connecte-t-il avec un utilisateur limité ou le même que le profil administrateur ?

Dans le même ordre d'idées, une application, même web, peut faire appel à des applicatifs par des appels systèmes. Après tout, quoi de mieux que Graphviz pour générer des graphes, et en SVG dynamique s'il vous plaît ? Oui, mais s'il est mal implémenté, une attaque peut réaliser des exécutions malicieuses directement au niveau du système. La vigilance est semblable à celle de l'intégrité des requêtes SQL : cacher les exécutions, et en limiter la portée.

Enfin, le vieil adage du "vivons heureux, vivons cachés" se doit d'être respecté dans le développement d'applications ouvertes ainsi vers le web. Il arrive ainsi encore trop fréquemment que des informations sensibles transitent en clair, voir s'affichent dans les paramètres de requêtes. Combien d'applications ne font qu'un contrôle sommaire de connexion, puis ne se basent pour l'accès aux données que sur l'identifiant de l'utilisateur ? Identifiant passé en paramètre de la requête et modifiable à souhaits, évidemment...

Dans le même ordre d'idées, il est bien connu que les serveurs de production doivent être configurer de manière à être le moins loquaces possible. Il doit en être de même pour les applications, surtout lors de la gestion des erreurs. En production, les informations sur les plantages et erreurs, si pratiques affichées à l'écran, ont plus leur place dans des logs.

La gestion des ressources

La gestion des ressources peut être perçue à plusieurs niveaux. Les langages de bas niveau tel que le C, continuent à être sujet aux erreurs de type buffer overflow. La méfiance se doit d'être de mise pour les applications critiques même si elles sont écrites en un langage de plus haut niveau car leur implémentation est bien souvent en C.

Au niveau de l'applicatif, il conviendra d'être vigilant au transit des données. Ainsi, il est encore courant de voir des données critiques, par exemple relatives à l'authentification, stockées sur le poste client et transiter en clair. Interceptées, elles peuvent donner à un tiers des droits d'administration. Dans le même ordre d'idée, il est courant de voir des chemins d'accès construits à partir des requêtes et donc facilement modifiables, ce qui peut mettre en danger le système de fichiers du serveur. La protection contre l'injection de code ou contre l'exécution de code non souhaité est encore couramment observée. Enfin, bien souvent encore, les cas d'école d'oubli ou de mauvaise initialisation de variables ainsi que le mauvais contrôle des calculs sont sujets à nombre d'erreurs. Bien qu'ils puissent paraître bénins, combinés à une mauvaise gestion des erreurs, il peut s'agir de cas qui entraînent un plantage des serveurs ou une remontée d'informations critiques.

Mauvaise sécurité

Les simples brèches de sécurité sont encore monnaie courante. Cela commence par une mauvaise gestion du contrôle d'accès. Il est ainsi conseillé de bien définir les rôles des utilisateurs, mais également d'être vigilant aux autorisations horizontales : autoriser un utilisateur ne dispense pas de vérifier qu'il accède bien à ses informations et non à celles du voisin. Ce contrôle d'accès peut être mis à mal par l'utilisation de méthodes de chiffrement fragiles ou par la gestion en dur des informations d'authentification. De même, la gestion des privilèges d'exécution de l'application est souvent simplifiée pour ne pas dire bâclée. Une application qui a besoin d'un certain niveau de privilèges élevé sur un système pour effectuer certaines opérations ne doit pas le rester indéfiniment.

Enfin, avec l'avènement des clients riches, il est tentant de se reposer sur eux pour le contrôle des informations. Mais il ne faut pas oublier que ces contrôles sont exposés et très facilement modifiables. Chaque composant (client, serveur...) doit contrôler sa propre intégrité.

En conclusion (ou si vous ne devez lire qu'une partie, c'est celle là).

J'ai volontairement choisi de ne pas lister précisément les différents points de l'article du CWE ni de d'entrer dans les détails des différents points afin de vous inciter à aller voir les différentes fiches. Chaque fiche CWE (Common Weakness Enumeration) possède un descriptif du risque avec des exemples type et des pistes pour y remédier.

Cependant, à bien y regarder, chaque faiblesse paraît relativement bénigne et souvent ne pas avoir d'effet néfaste. Mais le fait est que bien souvent, il suffit de les combiner entre elles pour voir tous les dommages qu'elles peuvent causer. Prenons le cas d'un contrôle d'accès non géré horizontalement (un utilisateur A est identifié avec certains droits, le système vérifie si il a le droit d'exécuter les différentes actions mais pas si ces actions concernent ses données ou celles d'un utilisateur B de même niveau d'accréditation). Un développeur trop sûr de lui pourra se dire qu'il contrôle au niveau applicatif les accès de cet utilisateur et qu'il n'y a pas besoin de plus de sécurité. Sauf qu'il (ou un autre développeur reprenant le code) utilise pour la navigation entre différentes pages web l'identifiant de l'utilisateur en tant que paramètre de la requête. Un utilisateur mal intentionné remarquera qu'une fois authentifié, en ne modifiant que ce simple paramètre, il se donne accès à tous les comptes des utilisateurs.

Ainsi, il convient d'appliquer quelques principes pourtant de base. Diffuser le minimum d'informations et contrôler les échanges. Ne pas traiter une information non vérifiée et surtout vérifier qu'elle n'entraîne pas de sortie du périmètre d'exécution de l'application. Mais surtout, ne pas oublier qu'une application s'exécute dans un certain contexte (sur un système sur lequel elle fait appel à d'autres ressources). Dans ce cadre, il convient de s'assurer que les différents composants se sécurisent eux même : les bases de données doivent assurer leur intégrité et ne pas se reposer sur l'applicatif. Dans le même ordre d'idées, elles ne doivent pas donner des privilèges d'administrateur à toutes les applications en permanence. Il en va de même pour les logiciels externes de l'application et du système de fichier : l'application doit s'exécuter dans un périmètre défini avec des droits définis.

Il s'agit de règles simples, simples à mettre en œuvre mais hélas bien souvent oubliées.

Liens :

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.