Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

CoqBlog

.NET is good :-)
{ Blog de coq }

Actualités

XPath : utilisez des requêtes paramétrées

Vous connaissez l'injection SQL ? En XPath, vous êtes exposé à la même chose, avec un risque de fuite de donnée plus important du fait de l'absence de restriction d'accès. A partir de là il devient possible de récupérer l'ensemble des données : vous en avez peut être entendu parler sous la dénomination "Blind XPath Injection". Si vous bâtissez des requêtes XPath avec des entrées utilisateur par simple concaténation de chaînes de caractères, vous êtes exposé à ce risque d'injection XPath.
D'ailleurs vous vous en êtes sans doute déjà aperçu, sans pour autant faire le parallèle direct avec le problème de sécurité, lorsque vous vous êtes retrouvés avec une requête invalide dû à la présence d'une apostrophe dans le texte utilisé.
Sans doute vous êtes vous juste posée la question "Comment échapper cet apostrophe ?", et peut être avez-vous appliqué la solution de remplacer, dans la requête, les apostrophes de délimitation des chaînes par des guillemets.

Une solution plus viable existe : de la même manière qu'avec SQL, nous allons utiliser des requêtes paramétrées.

Pour les exemples nous allons utiliser les données suivantes (notez l'inspiration...) :

<Persons> <Person ID="1"> <FirstName>Jean</FirstName> <LastName>Dupond</LastName> <Gender>H</Gender> </Person> <Person ID="2"> <FirstName>Jeanne</FirstName> <LastName>Dupond</LastName> <Gender>F</Gender> </Person> <Person ID="3"> <FirstName>Jean</FirstName> <LastName>Durand</LastName> <Gender>H</Gender> </Person> <Person ID="4"> <FirstName>Jeanne</FirstName> <LastName>Durand</LastName> <Gender>F</Gender> </Person> <Person ID="5"> <FirstName>Jeremy</FirstName> <LastName>D'urand</LastName> <Gender>H</Gender> </Person> </Persons>

 

 

Définition de la requête par concaténation

Admettons que vous voulez effectuer une recherche par début de nom, si vous avez décidé de procéder par concaténation, vous allez peut être procéder de la sorte :

public class Persons : IPersons { public Persons(String xmlFilePath) { this._persons = new XPathDocument(xmlFilePath); } private XPathDocument _persons; public IEnumerable GetFirstNameByLastName(String name) { // Définition de la requête, // par insertion de la valeur directement dans la requête String query = String.Format( "/Persons/Person[starts-with(LastName, '{0}')]/FirstName", name); // Récupération des données XPathNavigator nav = this._persons.CreateNavigator(); XPathNodeIterator persons = nav.Select(query); while (persons.MoveNext()) { yield return persons.Current.Value; } } }

Effectuons maintenant une recherche avec les termes "Dur" puis "D'u" :

private static void RunTest(IPersons persons, String name) { IEnumerable names = null; Console.WriteLine("Recherche de \"{0}\" : ", name); try { names = persons.GetFirstNameByLastName(name); foreach (String firstName in names) { Console.Write("\t"); Console.WriteLine(firstName); } } catch (Exception ex) { Console.Write("\t"); Console.WriteLine(ex.Message); } }

Le résultat est sans appel :

XPath - 001

Ici nous avons simplement provoquer une erreur. Imaginons maintenant que l'utilisateur aie l'idée de saisir ceci : "')]/ancestor::*|/Persons/Person[starts-with(LastName, 'blabla"
La requête devient : "/Persons/Person[starts-with(LastName, '')]/ancestor::*|/Persons/Person[starts-with(LastName, 'blabla')]/FirstName"
La sortie sera : "JeanDupondHJeanneDupondFJeanDurandHJeanneDurandFJeremyD'urandH"
Soit le texte de tous les noeuds de notre document, certes de façon peu lisible, mais une fuite de données quand même, et vous n'êtes pas à l'abri d'un attaquant plus motivé.

Nous allons maintenant voir comment remplacer cette requête "dynamique" par une requête paramétrée, à la fois de la manière "dure" avec l'implémentation manuelle de la chose, puis la manière "douce" avec ce que propose la librairie Mvp.Xml.

 

 

Requête paramétrée : utilisation d'un contexte personnalisé

Un paramètre dans une requête XPath s'écrit via le symbole "dollar" ($) suivi du nom de la variable. Notre requête aura donc cette forme :
"/Persons/Person[starts-with(LastName, $lastname)]/FirstName"

Afin d'utiliser des paramètres dans notre requête XPath, nous allons devoir utiliser un contexte personnalisé permettant au processeur XPath de déterminer les informations nécessaires sur la variable à l'execution.

Nous allons avoir affaire aux éléments suivants : 

  • Classe XsltContext
    Classe que nous devons implémenter et dont nous associerons une instance à notre expression pour que le processeur XPath puisse résoudre les variables.
    Elle définit 4 méthodes (CompareDocument, PreserveWhitespace, ResolveFunction, et ResolveVariable) et une propriété (Whitespace).
    Dans notre cas, nous allons nous intéresser principalement à ResolveVariable, qui va nous permettre de gérer la résolution des variables insérées dans nos requêtes paramétrées.
  • Interface IXsltContextVariable
    Interface que doit implémenter la classe dont une instance doit être renvoyée par la méthode ResolveVariable citée précédemment.
    Elle définit une méthode (Evaluate) et 3 propriétés (IsLocal, IsParam et VariableType).

Vous trouverez en pièce jointe dans les sources associées sur CSharprFR une implémentation d’un contexte personnalisé, la classe CustomContext, relativement simple dans le sens où nous ne nous occupons pas de certains éléments, comme les fonctions personnalisés.

Voici maintenant une version de notre classe Persons l'utilisant :

public class PersonsParameterized : IPersons { public PersonsParameterized(String xmlFilePath) { this._persons = new XPathDocument(xmlFilePath); } private XPathDocument _persons; private XPathExpression _expression = XPathExpression.Compile( "/Persons/Person[starts-with(LastName, $lastname)]/FirstName" ); public IEnumerable GetFirstNameByLastName(String name) { // Création du contexte et ajout de la variable CustomContext context = new CustomContext(); context.AddVariable("lastname", name); // Association du contexte à l'expression this._expression.SetContext(context); // Récupération des données XPathNavigator nav = this._persons.CreateNavigator(); XPathNodeIterator persons = nav.Select(this._expression); while (persons.MoveNext()) { yield return persons.Current.Value; } } }

Et nous obtenons bien ce que nous voulions :

XPath - 002

 

 

Requête paramétrée : utilisation de la librairie Mvp.Xml

La librairie Mvp.Xml est un projet de plusieurs MVPs destiné à étendre les capacités de la BCL du Framework .NET.
XPathMania, un addin pour Visual Studio permettant d'executer des requêtes XPath directement dans l'IDE fait également partie de ce projet.

Ici nous nous intéressons à la classe XPathCache, qui nous permet justement d'utiliser des requêtes paramétrées très simplement, l'implémentation du contexte personnalisé et tout ce qui y touche étant déjà réalisé en interne.

Une implémentation utilisant cette classe XPathCache pourrait ressembler à ceci et permet bien sûr d'obtenir le même résultat qu'avec notre implémentation de contexte personnalisé :

public class PersonsXPathCache : IPersons { public PersonsXPathCache(String xmlFilePath) { this._persons = new XPathDocument(xmlFilePath); } private const String EXPRESSION = "/Persons/Person[starts-with(LastName, $lastname)]/FirstName"; private XPathDocument _persons; public IEnumerable GetFirstNameByLastName(String name) { // Récupération des données XPathNodeIterator persons = XPathCache.Select( EXPRESSION, this._persons.CreateNavigator(), new XPathVariable("lastname", name) ); while (persons.MoveNext()) { yield return persons.Current.Value; } } }

 

 

Ressources associées

Sources sur CSharpFR : XPATH : UTILISEZ DES REQUÊTES PARAMÉTRÉES 

 

 

Liens intéressants

 

Petite parenthèse, qui reste cependant dans le sujet : si vous vous servez d'entrées utilisateur pour bâtir une expression régulière, pensez à utiliser la méthode RegEx.Escape.

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: dimanche 18 mars 2007 11:45 par coq
Classé sous : , , , , ,

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01