Comment utiliser des variables dans une instruction SQL en Python?

91

Ok donc je ne suis pas très expérimenté en Python.

J'ai le code Python suivant:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

var1est un entier, var2& var3sont des chaînes.

Comment puis-je écrire les noms de variables sans python en les incluant dans le texte de la requête?

user111606
la source

Réponses:

103
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

Notez que les paramètres sont passés sous forme de tuple.

L'API de base de données échappe et cite correctement les variables. Veillez à ne pas utiliser l'opérateur de formatage de chaîne ( %), car

  1. il n'échappe ni ne cite.
  2. il est sujet aux attaques de format de chaîne non contrôlées, par exemple l' injection SQL .
Ayman Hourieh
la source
Intéressant, pourquoi fonctionne-t-il avec les vars séparément plutôt que dans un tableau (var1, var2, var3)?
Andomar
Selon les spécifications de l'API DB, il semble que cela puisse être dans les deux cas: python.org/dev/peps/pep-0249
Ayman Hourieh
9
@thekashyap Lisez à nouveau attentivement. Ce qui n'est pas sûr, c'est l'utilisation de l'opérateur de formatage de chaîne %. En fait, je le dis dans la réponse.
Ayman Hourieh
mon mauvais .. j'ai imaginé un % au lieu d' ,entre la chaîne et les variables .. je ne peux pas annuler mon vote pour diverses raisons .. Personnellement, j'aimerais voir des mots comme non sécurisé / attaque, etc. mentionnés dans la description où vous dites don 't use %..
Kashyap
1
@eric la réponse dit de ne pas utiliser l' % opérateur pour formater la chaîne. Ceux %de la chaîne sont utilisés cursor.executedirectement par , et comme il sait qu'il génère du SQL, il peut faire plus pour vous protéger.
Mark Ransom
66

Différentes implémentations de l'API DB Python sont autorisées à utiliser différents espaces réservés, vous devrez donc savoir lequel vous utilisez - cela pourrait être (par exemple avec MySQLdb):

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

ou (par exemple avec sqlite3 de la bibliothèque standard Python):

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))

ou d'autres encore (après VALUESvous pourriez avoir (:1, :2, :3), ou "styles nommés" (:fee, :fie, :fo)ou (%(fee)s, %(fie)s, %(fo)s)où vous passez un dict au lieu d'une carte comme deuxième argument à execute). Vérifiez la paramstyleconstante de chaîne dans le module de l'API DB que vous utilisez et recherchez le paramètre de style sur http://www.python.org/dev/peps/pep-0249/ pour voir quels sont tous les styles de passage de paramètres!

Alex Martelli
la source
Est-il possible de faire la même chose mais avec le script SQL externe?
Novitoll
46

Plusieurs façons. N'utilisez PAS le plus évident ( %savec %) dans le vrai code, il est ouvert aux attaques .

Ici, copier-coller à partir de pydoc de sqlite3 :

# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

Plus d'exemples si vous avez besoin:

# Multiple values single statement/execution
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', ('RHAT', 'MSO'))
print c.fetchall()
c.execute('SELECT * FROM stocks WHERE symbol IN (?, ?)', ('RHAT', 'MSO'))
print c.fetchall()
# This also works, though ones above are better as a habit as it's inline with syntax of executemany().. but your choice.
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', 'RHAT', 'MSO')
print c.fetchall()
# Insert a single item
c.execute('INSERT INTO stocks VALUES (?,?,?,?,?)', ('2006-03-28', 'BUY', 'IBM', 1000, 45.00))
Kashyap
la source
4
Certaines implémentations DB-API utilisent en fait% s pour leurs variables, notamment psycopg2 pour PostgreSQL. Cela ne doit pas être confondu (bien que cela soit facile) avec l'utilisation de% s avec l'opérateur% pour le remplacement de chaîne. Je serais vraiment bien si, pour la portabilité, nous pouvions simplement avoir une manière standard définie de spécifier les paramètres SQL pour DB-API.
ThatAintWorking
24

http://www.amk.ca/python/writing/DB-API.html

Soyez prudent lorsque vous ajoutez simplement des valeurs de variables à vos instructions: Imaginez un utilisateur se nommant ';DROP TABLE Users;'- C'est pourquoi vous devez utiliser l'échappement SQL, que Python vous fournit lorsque vous utilisez le curseur.execute de manière décente. Exemple dans l'URL:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )
Verrouillage numérique
la source
13
En fait, ce n'est pas un échappement SQL. C'est une liaison variable, qui est beaucoup plus simple et directe. Les valeurs sont liées dans l'instruction SQL après l'analyse, ce qui la rend immunisée contre toute attaque par injection.
S.Lott
Eh bien, qu'il s'agisse d'un échappement SQL ou d'une liaison de variable dépend de la qualité de votre serveur de base de données / pilote DB-API. J'ai vu des bases de données de production largement déployées dans le monde réel dont le pilote DB-API s'échappe, plutôt que de garder les données et le code hors bande sur le fil. Inutile de dire que je n'ai pas beaucoup de respect pour ces soi-disant «bases de données».
Charles Duffy