2017-06-15 1 views
0

J'ai une petite application Web Python/Flask qui retourne des données d'une table SQLite au format CSV. J'ai trouvé qu'il est plus rapide d'utiliser le programme de ligne de commande sqlite3 pour exporter les résultats de la requête directement dans un fichier CSV puis retourner le fichier CSV que pour lire les résultats de la requête en Python, les écrire dans un fichier en mémoire, puis retourner le fichier en mémoire. Cependant, l'utilisation du programme sqlite3 signifie que je dois créer les requêtes moi-même, en soumettant mon application aux attaques par injection SQL.Construire une requête SQLite en Python mais l'exécuter en utilisant le programme de ligne de commande sqlite3?

plus rapide, mais vulnérables aux attaques par injection SQL

queries = """ 
.mode csv 
.headers on 
.output /tmp/results.csv 
SELECT * FROM mytable WHERE foo = '{0}'; 
""".format(user_input) 

subprocess.check_output(
    ["sqlite3", "/path/to/mydb.sqlite"], input=bytes(queries.encode("utf-8"))) 

return send_file("/tmp/results.csv", mimetype="text/csv") 

Safe de l'injection SQL, mais plus lent

conn = sqlalchemy.create_engine("sqlite:////path/to/mydb.sqlite") 
result = conn.execute("SELECT * FROM mytable WHERE foo = ?", (user_input,)) 

csvfile = io.StringIO() 
csvwriter = csv.writer(csvfile) 
csvwriter.writerow(result.keys()) 

for row in result.fetchall(): 
    csvwriter.writerow(row) 

return Response(csvfile.getvalue(), mimetype="text/csv") 

Est-il possible que je peux utiliser une bibliothèque Python (sqlalchemy, le module sqlite3, ou toute autre chose) pour construire une requête qui soit sûre de l'injection SQL mais qui n'exécute pas réellement la requête, de sorte que je puisse à la place exécuter la requête via un sous-processus en utilisant le sqlite3 programme de ligne de commande?

Répondre

0

A directe SQLite à CSV chemin d'exportation (en utilisant le sqlite3 du programme .mode csv) aura un avantage de rapidité sur un chemin d'exportation SQLite-to-Python-to-CSV plus détourné principalement dans le cas de la sélection de grandes quantités de données; lors de la sélection de plus petites quantités de données, la vitesse ne diffère pas autant. Donc en fonction de votre schéma, une option serait d'utiliser Python pour interroger la base de données pour un ensemble d'ID, puis utilisez sqlite3 pour sélectionner le reste des données en utilisant les ID. De cette façon, les bibliothèques Python désinfecteront votre entrée utilisateur (empêchant l'injection SQL, etc.) et ce que vous passez au programme sqlite3 ne sera pas une entrée de l'utilisateur mais des ID que vous avez vous-même interrogés.

conn = sqlalchemy.create_engine("sqlite:////path/to/mydb.sqlite") 
result = conn.execute("SELECT ID FROM mytable WHERE foo = ?", (user_input,)) 
ids = [row[0] for row in result.fetchall()] 

queries = """ 
.mode csv 
.headers on 
.output /tmp/results.csv 
SELECT * FROM mytable WHERE ID IN ({0}); 
""".format(",".join([str(id) for id in ids])) 

subprocess.check_output(
    ["sqlite3", "/path/to/mydb.sqlite"], input=bytes(queries.encode("utf-8"))) 

return send_file("/tmp/results.csv", mimetype="text/csv") 

Cette approche implique 2 requêtes au lieu de 1, mais la différence peut être marginale si le goulot d'étranglement sélectionne toutes les données. Profil votre application pour le savoir à coup sûr.

0

A l'intérieur d'une chaîne SQL, le seul caractère spécial est la citation ' elle-même. (Et le caractère de code 0, ce qui mettrait fin à toute requête et le résultat d'une erreur de syntaxe.)

Pour échapper à des guillemets simples, doubler:

user_input.replace("'", "''")