IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Services Web Discussion :

WCF RIA Services Injection SQL


Sujet :

Services Web

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Mai 2007
    Messages
    31
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 31
    Par défaut WCF RIA Services Injection SQL
    Bonsoir.

    Je travail avec Entity Framework 5, Silverlight 5, WCF RIA Services, MySQL 5.6 and MySQLConnector 6.5.6.

    J'ai découvert que la portion de code suivant générait des erreurs de syntax côté serveur MySQL lorsque dans la chaîne de caractère passée en paramètres il y a une quote.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    DomainContext.Load<Product>(DomainContext.GetProductQuery()
                                             .Where<Product>(p => p.name.Contains(parameter))
                                             .Take<Product>(30));
    Ici je recherche tous les produits contenant un certains motif.
    Or si j'utilise une égalité stricte comme ceci je n'ai pas d'erreur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    DomainContext.Load<Product>(DomainContext.GetProductQuery()
                                             .Where<Product>(p == parameter)
                                             .Take<Product>(30));
    Ceci est la méthode appelée côté serveur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public IQueryable<Product> GetProduct()
    {
        return this.ObjectContext.product;
    }
    Je suis pour le moins perplexe. Je pensais vraiment que EF utilisait des requêtes paramétrées.
    Je ne vois vraiment pas comment résoudre ce problème.
    Réécrire des centaines de méthodes côté service pour inclure un filtre avec un résultat hasardeux me parait pour le moins risqué.

    Toute aide sera vraiment grandement appréciée.
    Bonne soirée à tous !

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par Binenebi Voir le message
    Je suis pour le moins perplexe. Je pensais vraiment que EF utilisait des requêtes paramétrées.
    Ca dépend du provider. Celui de SQL Server utilise des requêtes paramétrées, mais historiquement le provider ADO.NET pour MySQL a toujours été un peu bancal, donc ça m'étonne pas vraiment de voir ce genre de chose...

  3. #3
    Membre averti
    Inscrit en
    Mai 2007
    Messages
    31
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 31
    Par défaut
    Je vais voir du côté de MySQL dans ce cas.
    Ceci dit, c'est grave si je peux même pas sécuriser mon service WEB face à ce type d'attaque basique.
    Merci !

  4. #4
    Membre émérite Avatar de chamamo
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    588
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 588
    Par défaut
    Elle ressemble à quoi la requête générée par ton provider?
    As-tu essayé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    p.name.Contains(parameter.Replace("'","''"))
    ?

  5. #5
    Membre averti
    Inscrit en
    Mai 2007
    Messages
    31
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 31
    Par défaut
    Effectivement, j'ai déjà des solutions de ce type pour protéger le client.
    Le problème c'est que le service est toujours vulnérable puisque n'importe qui peut potentiellement effectuer une requête sur le service.

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Par défaut
    Citation Envoyé par Binenebi Voir le message
    Effectivement, j'ai déjà des solutions de ce type pour protéger le client.
    Le problème c'est que le service est toujours vulnérable puisque n'importe qui peut potentiellement effectuer une requête sur le service.
    Effectivement. Il faudrait donc faire cette manip du côté du service, et non du côté du client... Le problème c'est que tu renvoies directement un IQueryable, et donc tu ne contrôles pas grand chose.

    J'ai une petite idée pour régler ça, mais comme je connais pas trop WCF RIA Services, je suis pas certain que ça marcherait... Enfin je te la donne quand même, tu verras bien ce que ça donne.

    En gros, l'idée serait de ne pas renvoyer directement return this.ObjectContext.product, mais un wrapper qui implémente IQueryable et se charge de faire la correction dans les appels à Contains, avec un ExpressionVisitor.

    Voilà une implémentation un peu expérimentale, qui semble fonctionner au moins avec Linq to Objects :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    class MySqlQueryableWrapper<T> : IQueryable<T>
    {
        private readonly IQueryable<T> _queryable;
        private readonly IQueryProvider _provider;
     
        public MySqlQueryableWrapper(IQueryable<T> queryable)
        {
            _queryable = queryable;
            _provider = new MySqlQueryProviderWrapper(queryable.Provider);
        }
     
        public Type ElementType
        {
            get { return _queryable.ElementType; }
        }
     
        public Expression Expression
        {
            get { return _queryable.Expression; }
        }
     
        public IQueryProvider Provider
        {
            get { return _provider; }
        }
     
        public IEnumerator<T> GetEnumerator()
        {
            return _queryable.GetEnumerator();
        }
     
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
     
    class MySqlQueryProviderWrapper : IQueryProvider
    {
        private readonly MySqlExpressionFixer _visitor = new MySqlExpressionFixer();
        private readonly IQueryProvider _provider;
     
        public MySqlQueryProviderWrapper(IQueryProvider provider)
        {
            _provider = provider;
        }
     
        public IQueryable CreateQuery(Expression expression)
        {
            return _provider.CreateQuery(_visitor.Visit(expression));
        }
     
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            return _provider.CreateQuery<TElement>(_visitor.Visit(expression));
        }
     
        public object Execute(Expression expression)
        {
            return _provider.Execute(_visitor.Visit(expression));
        }
     
        public TResult Execute<TResult>(Expression expression)
        {
            return _provider.Execute<TResult>(_visitor.Visit(expression));
        }
     
    }
     
    class MySqlExpressionFixer : ExpressionVisitor
    {    
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node.Method.Name == "Contains" &&
                node.Method.DeclaringType == typeof(string) &&
                node.Arguments.Count == 1)
            {
                var c = node.Arguments[0] as ConstantExpression;
                if (c != null)
                {
                    string s = c.Value as string;
                    if (s != null)
                    {
                        s = s.Replace("'", "''");
                        node = Expression.Call(node.Object, node.Method, Expression.Constant(s));
                    }
                }
            }
     
            return base.VisitMethodCall(node);
        }
    }

    Pour l'utiliser, tu remplaces ça :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public IQueryable<Product> GetProduct()
    {
        return this.ObjectContext.product;
    }

    Par ça :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public IQueryable<Product> GetProduct()
    {
        return new MySqlQueryableWrapper<Product>(this.ObjectContext.product);
    }

    Et le test que j'ai fait pour vérifier si ça marchait :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        var q1 = new[] { "salut", "l'ami 1", "l''ami 2" }.AsQueryable();
        var q2 = new MySqlQueryableWrapper<string>(q1);
        var result1 = q1.Where(s => s.Contains("l'ami")).FirstOrDefault(); // "l'ami 1"
        var result2 = q2.Where(s => s.Contains("l'ami")).FirstOrDefault(); // "l''ami 2"

    Bon, ça reste du gros bricolage, il vaudrait mieux corriger le provider MySQL, mais enfin c'est toujours mieux que de laisser une faille de sécurité béante...

Discussions similaires

  1. Réponses: 27
    Dernier message: 11/08/2011, 22h12
  2. WCF RIA Services
    Par Kais. dans le forum Silverlight
    Réponses: 3
    Dernier message: 26/04/2010, 21h40
  3. Réponses: 6
    Dernier message: 22/12/2009, 21h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo