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

Python Discussion :

Quotes avec une requête sqlite3


Sujet :

Python

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Par défaut Quotes avec une requête sqlite3
    Bonjour à tous.

    Mon but est de remplacer les valeurs d'un champ d'une table sqlite.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    sql = u"update %s set %s=\"%s\" where id=\"%i\"" %(table, col, tab, idt)
    cursor.execute(sql)
    Le code ne marche pas car certaines valeurs du champ contiennent des « " ». Mais remplacer \"%s\" et \"%i\" par \'%s\' et \'%i\' respectivement, ne marche pas non plus car certaines valeurs contiennent des « ' ». Certaines valeurs contiennent à la fois des « " » et des « ' ».

    Y a-t-il une solution ?

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 695
    Par défaut lire la doc
    Je n'ai pas testé mais, plutôt que d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    sql = u"update %s set %s=\"%s\" where id=\"%i\"" %(table, col, tab, idt)
    cursor.execute(sql)
    je ferais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    stmt=u"update ? set ?=? where id=?"
    cursor.execute(sql, (table, col, tab, idt))
    La différence est subtile mais dans le premier cas, on construit une chaine de caractères que l'on passe à la DBAPI.
    La conversion Python => DB est faite par votre code.
    Dans le 2ème cas, vous donnez à la DBAPI, un uplet de variables typées et vous lui déléguez la conversion qui sera réalisée dans 'un contexte' approprié.
    -W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Par défaut
    Bonsoir wiztricks.

    Le bogue apparait alors même pour les valeurs sans « ' » ou « " » :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Traceback (most recent call last):
      File "remplace.py", line 18, in <module>
        cursor.execute(u"update ? set ?=? where id=?", (table, col, tab, idt))
    sqlite3.OperationalError: near "?": syntax error

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Par défaut
    Il semblerait que les "?" ne soient valables que pour les valeurs (ex: clé, chaines de caractères d'une case) et pas pour les noms de table ou de colonne.

    Usually your SQL operations will need to use values from Python variables. You shouldn’t assemble your query using Python’s string operations because doing so is insecure; it makes your program vulnerable to an SQL injection attack.
    Instead, use the DB-API’s parameter substitution. Put ? as a placeholder wherever you want to use a value, and then provide a tuple of values as the second argument to the cursor’s execute() method. (Other database modules may use a different placeholder, such as %s or :1.)
    http://docs.python.org/library/sqlite3.html

    Il n'est question que de valeurs (values).


    Ce qui donnerait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cursor.execute(u"update maTable set maColonne=? where id=?", (tab, idt))
    Ça fonctionne.

    Merci beaucoup, wiztricks.

  5. #5
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 695
    Par défaut
    Bravo.
    Ceci dit, il faudrait écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    stmt = u'update %s set %s=? where id=?" % (maTable, maColonne)
    cursor.execute(stmt, (tab, idt))
    1. stmt 'fige' le paramétrage des meta (table, colonne) - les %s
    2. exécute l'applique a une liste de tuples, des datas- les ?


    Ces concepts ne sont pas intuitifs, il faut se les approprier: c'est un apprentissage...
    En proposant, le remplacement de:
    • "update %s set %s=%s where id=%s", par
    • "update ? set ?=? where id=?"

    Je voulais faire toucher les deux dimensions de tout accès à une base (meta et data) et de leur contexte.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    J'ai cherché une solution en restant dans le cadre d'un recours au formatage de chaîne avec une petite idée, mais elle s'est révélée inopérante.


    Python fait sur le contenu de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    conn.cursor().execute("update stocks set symbol='RHET' where id='KKL' ")
    un traitement de chaîne comme à l'ordinaire, notamment en interprétant les deux caractères " en tête et en queue comme des délimiteurs de la chaîne.
    Les caractères ' sont eux aussi interprétés comme des délimiteurs, mais seulement après passage de la chaîne à l'API.

    En faisant des échappements, on peut passer des valeurs de champs contenant " ou ' :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    conn.cursor().execute("update stocks set symbol='RH\"ET' where id=\"KK'L\" ")
    conn.cursor().execute('update stocks set symbol=\'RH"ET\' where id="KK\'L'' ')
    J'ai pensé que des caractères \ passés à l'API pouvaient échapper les caractères considérés comme délimiteurs par l'API. Il n'en est rien. Le fait qu'il s'agisse de délimiteurs dans les deux cas ne suffit pas à faire que ce soit efficace.

    À partir de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    conn.cursor().execute('update stocks set symbol="DD\'EEYY" where id="31" ')
    que l'on écrive symbol = "DD\'EE"YY" ou symbol = "DD\'EE\"YY" , Python envoie à l'API la chaîne interprétée
    update stocks set symbol="DD'EE"YY" where id="31"
    et l'API trouvera donc une valeur DD'EE pour le champ symbol, puis ne saura plus interpréter ce qui se trouve après "DD'EE" dans la chaîne.

    J'ai donc essayé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    conn.cursor().execute("update stocks set symbol=\"luc'er\\\"y\" where id=\"**\" ")
    mais ça ne marche pas plus.

    Conclusion: \ n'est pas un caractère d'échappement dans le cadre de l'API.

    Le formatage de chaîne occulte un peu ce qui se passe, mais ne change rien de fondamental: le procédé de passage d’une valeur via une chaîne (formatée ou non) passée à l’API trouve sa limite quand la valeur peut comporter à la fois un caractère " et un caractère '

    J'aime bien comprendre précisément les choses.






    Heureusement il y a la possibilité de passer des valeurs à des champs en injectant les valeurs à partir d'un tuple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    conn.cursor().execute('insert into stocks values (?,?,?,?,?,?)', t)
    # ou
    conn.cursor().execute('update stocks set symbol=? where id=? ',(a,b))

    Soit dit en passant, ? n'est valable que pour les valeurs;
    mais après essais, il apparaît que:

    - il n'est pas possible de créer une colonne ou une table avec un nom comportant ' ou "

    - il est possible de créer une database avec un nom comportant ' ou ", mais ensuite je n'ai pas réussi à faire jouer une opération update dans cette database, même avec son nom écrit en dur

    La limitation de ? aux valeurs n'est donc pas une grande contrainte.







    Question de curiosité:

    You shouldn’t assemble your query using Python’s string operations because doing so is insecure; it makes your program vulnerable to an SQL injection attack.
    Qu'est-ce que ça signifie ?

    C'est quoi une «SQL injection attack» ?

  7. #7
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Merci pour ces précisions wiztricks
    Je n'avais pas vu ton post avant de mettre le mien précédent.


    Est-ce que je comprends bien si je retiens qu'il y a dans une data base:

    - des metadonnées:
    noms des différentes tables,
    et pour chaque table: nombre et noms des colonnes

    - des données brutes:
    les données proprement dites contenues dans les enregistrements = lignes (possédant toutes le même nombre de colonnes dans une table précise)



    Est-ce que la page suivante
    http://www.databasejournal.com/featu...data-Views.htm
    a vraiment un rapport avec cette distinction ?

  8. #8
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    Une injection SQL est une attaque qui consiste à "dévier" les valeurs insérées dans la requête pour pouvoir effectuer des requêtes arbitraires sans l'accord du propriétaire de la DB.

    Un exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT * FROM projects WHERE project_id = %s
    On s'attend évidemment à ce que le %s soit remplacé par un identifiant quelconque (typiquement, un entier). Il est même fort possible que cet identifiant soit fourni, d'une manière ou d'une autre, par un utilisateur du logiciel.

    La plupart du temps, les utilisateurs donneront des données correctes, ce qui résultera en une query tout à fait valide. Par exemple, s'il nous fournit l'entier 5:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT * FROM projects WHERE project_id = 5
    Hélas, la vie n'est pas faite que de bonnes chose, et nombreux sont les gens qui essayeront de pirater votre logiciel. Imaginez qu'il donne ceci:
    Ce qui donnerait la query:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT * FROM projects WHERE project_id = 1; DROP TABLE project
    Voila, monsieur le visiteur a supprimé toute votre table project. Il a été gentil, il aurait pu faire à peu près tout ce qu'il voulait (dépendant de certains paramètres).

    C'est pour ces injections SQL qu'il est intéressant de donner les arguments à part au gestionnaire de DB.
    Passons lui ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT * FROM projects WHERE project_id = ?
    Il interprète de suite la requête, et tient en mémoire qu'il s'agit d'un select, mémorise le nom de la table, des champs impliqués dans le Where, des champs choisis etc.

    Il retient également qu'il a besoin d'un argument, qui correspondra à la clause project_id = ?du Where. De cette manière, quoi que l'utilisateur donne comme valeur, le SGBD considèrera ça uniquement comme étant "ce à quoi doit être égal le champ project_id". Ca implique que l'injection SQL devient impossible.

    En plus de cette protection sans faille, il est certainement possible de stocker ces requêtes dites "préparées". Elle est alors parsée une seule fois par le SGBD, et il en ressort un gain de temps potentiellement énorme.


    Cette injection SQL est surtout de mise dans les services en ligne. Elle est bien connue de la plupart des développeurs web, et très spécialement les devs PHP, qui ont été fortement exposés à cette attaque.

  9. #9
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 695
    Par défaut
    eyquem,

    Il y a deux questions à se poser:
    - quelle est la syntaxe du nom d'une table ou d'une colonne et quels sont les caractères utilisables pour construire ces 'noms'. l'URL indique quelques unes de ces contraintes pour MSSQL
    - puisqu'il s'agit de 'meta' il faut que la syntaxe soit utilisable dans les différents langages de programmation utilisés... Et cohérente si on utilise plusieurs SGDB (les restrictions de MySQL ne sont peut être pas celles de PostGres,...)
    => il n'est peut être pas si grave de ne pas avoir ' ou " dans les noms de colonnes et de tables... Si?

    Note: si on travaille sur plusieurs SGDB, il est peut être aussi 'bon' de s'inquiéter sur la compatibilité des types de bases.

    Tu as fais beaucoup de tests pour essayer de faire via des chaînes de caractères des opérations de conversion de type_python vers 'string' alors que c'est un des grand avantages de DBAPI de convertir une variable de type_python dans le type correspondant de la BDD.
    1 - tant que c'est des strings pourquoi pas pour des types tels que datetime ou d'autres, ca se discute.
    2 - le passage par des chaînes de caractères rend difficile la vectorisation (ie traiter une liste de tuple au lieu d'un.

    Pour les attaques via injection voir l'URL.
    -W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Bravo.
    Ceci dit, il faudrait écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    stmt = u'update %s set %s=? where id=?" % (maTable, maColonne)
    cursor.execute(stmt, (tab, idt))
    En effet, il est possible de combiner les 2 types de caractères de remplacement.

    Citation Envoyé par wiztricks
    Tu as fais beaucoup de tests pour essayer de faire via des chaînes de caractères des opérations de conversion de type_python vers 'string' alors que c'est un des grand avantages de DBAPI de convertir une variable de type_python dans le type correspondant de la BDD.
    J'avais aussi fait quelques tests depuis l'interpréteur, mais on s'y perd vite avec les multiples \ " et '.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 11
    Dernier message: 28/12/2004, 16h15
  2. [Recordset] Incompatibilté de type avec une requête
    Par lbourlet dans le forum Access
    Réponses: 2
    Dernier message: 29/10/2004, 15h52
  3. PB avec une requête Count
    Par Marion dans le forum ASP
    Réponses: 7
    Dernier message: 05/07/2004, 12h56
  4. Pb avec une requête
    Par arsgunner dans le forum ASP
    Réponses: 4
    Dernier message: 14/06/2004, 08h40
  5. problème avec une requête imbriquée
    Par jaimepasteevy dans le forum Langage SQL
    Réponses: 13
    Dernier message: 05/12/2003, 10h29

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