Teile einer Abfrage über raw_Input()

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Donnerstag 6. Dezember 2007, 13:06

Ich möchte gern für bestimmt Einträge meiner Datenbank Berechnungen durchführen. Dafür dachte ich: "okay, dann lass ich den WHERE Teil des Selects einfach den Anwender selber bestimmen und frag das mit raw_input() ab.

Code sieht so aus:

Code: Alles auswählen

sel = raw_input("Formulieren Sie für die SQL-Abfrage die WHERE Clausel:\n" )

cursor.execute('select * from Indizes where s%' ,sel)
Eingabe + Fehlermeldung:

Code: Alles auswählen

Evaluating Indikatoren_loop.py
Passwort für MySQL: ********
Formulieren Sie ür die SQL-Abfrage die WHERE Clausel:
calc_cat = "a"
C:\Programme\Wing IDE 101 3.0\src\debug\tserver\_sandbox.py:25: Warning: Truncated incorrect INTEGER value: 'calc_cat = "a"'
calc_cat ist aber gar kein INT, sondern CHAR, mit Länge 1
Daher verstehe ich die Fehlermeldung nicht.

Und:

Code: Alles auswählen

>>> cursor.execute('select * from Indizes where calc_cat = "a"')
24L
>>> 
funktioniert ja auch.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 6. Dezember 2007, 15:44

`%s` oder `s%`? Das hast du in deinem oben geposteten Code falschrum.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Donnerstag 6. Dezember 2007, 15:49

Ausserdem musst du die einzusetztenden Daten mit % angeben, nicht Komma:

Code: Alles auswählen

'select * from Indizes where %s' % sel
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Donnerstag 6. Dezember 2007, 16:02

Leonidas hat geschrieben:`%s` oder `s%`? Das hast du in deinem oben geposteten Code falschrum.
*argggg* Danke.

EDIT: gerade bei mir im Code geguckt, da steht es aber richtig drin und gleiche Fehlermeldung, dann muss das umgedrehte beim Code hier rein posten passiert sein...

Code: Alles auswählen

'select * from Indizes where %s' % sel
Ich dachte dass sei böse... und gar nicht gut, aber vielleicht hab ich da auch was falsch verstanden
Zuletzt geändert von meneliel am Donnerstag 6. Dezember 2007, 16:05, insgesamt 1-mal geändert.
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Donnerstag 6. Dezember 2007, 16:03

Rebecca hat geschrieben:Ausserdem musst du die einzusetztenden Daten mit % angeben, nicht Komma:

Code: Alles auswählen

'select * from Indizes where %s' % sel
für Stringersetzungen, ja, aber ich denke, dass er da die funktionalität der funktion Execute nutzt (die sich um escaping kümmert und so), was in diesem Fall doch auch der bevorzugte Weg sein sollte.

edit: @meneniel, ich habe es auch so verstanden.
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Donnerstag 6. Dezember 2007, 16:07

Hab es jetzt aber so gemacht, wie von Rebecca vorgeschlagen und nun funktioniert es.

EDIT
Ich bin gerade ein ganz klein wenig verwirrt:

Code: Alles auswählen

cursor.execute('select AGS, Datum, AREA, ID from ebenen where (DES = %s)' % sel_ebene)
...
OperationalError: (1054, "Unknown column 'xyz' in 'where clause'")
ABER:

Code: Alles auswählen

>>> cursor.execute('select AGS, Datum, AREA, ID from ebenen where (DES = %s)' ,sel_ebene)
349L
nochmal EDIT:

nun kann es aber je nach Eingabe sein, dass sel_ebene mehr beeinhaltet als z.B. nur "xyz" sondern könnte auch drin stehen '"xyz" OR DES = "abc"'

für DEN Fall funktioniert dann nur wieder:

Code: Alles auswählen

cursor.execute('select AGS, Datum, AREA, ID from ebenen where (DES = %s)' % sel_ebene)
Was doof ist, hab extra nen dict. gemacht, das je nach USER - Eingabe (die müssen nur noch eine Zahl eingeben), dann den richtigen Ausdruck wählt). Heißt dass, ich müsste an dieser Stelle dann am bestennoch mal if/elif einfügen, je nach sel_ebene verschiedene Anweisungen für das execute wählen, oder gibt es auch eine Variante, mit der ich das Problem umgehen kann.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Freitag 7. Dezember 2007, 14:23

rebeccas Tipp ist aber wie keppla schrieb in diesem Fall, falsch. Er reißt dir eine klasse SQL-Injection-Lücke in dein Programm.

xkcds Exploits of a mum zeigt das ganz gut - um das "sanitize" kümmert sich `execute()`.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Freitag 7. Dezember 2007, 14:48

:oops:
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Freitag 7. Dezember 2007, 21:41

Was mich verwirrt ist, dass es manchmal nur mit "%variable" funktioniert und nicht mit ",variable"... warum?
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Freitag 7. Dezember 2007, 21:44

Weil das Datenbankmodul nicht überall Variablen zulässt - etwa bei Tabellennamen nicht, die möchte das Datenbankmodul statisch haben.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Sonntag 9. Dezember 2007, 10:54

Leonidas hat geschrieben:Weil das Datenbankmodul nicht überall Variablen zulässt - etwa bei Tabellennamen nicht, die möchte das Datenbankmodul statisch haben.
Das kann ich ja dann aber mit Stringersetzungen umgehen. Das heißt ich komme im Prinzip eh nicht um sie herum.

Gibt es eine Möglichkeit, in dem Beispiel, dass einheitlich hin zu bekommen? Weil an einigen Stellen, dass ja mit den Stringersetzungen auch nicht funktionierte. Mir ist dann in dem Fall wohl egal ob es böse ist. Es muss nur funktionieren. :(
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5554
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Telfs (Tirol)
Kontaktdaten:

Sonntag 9. Dezember 2007, 11:05

meneliel hat geschrieben:Weil an einigen Stellen, dass ja mit den Stringersetzungen auch nicht funktionierte.
Hallo meneliel!

Probier mal http://docs.python.org/lib/node40.html aus. Das nehme ich immer her, wenn in den Texten öfter ein %-Zeichen vorkommt und der Text mit der normalen Stringersetzung zu fehleranfällig wird.

mfg
Gerold
:-)
[url]http://halvar.at[/url] | [url=http://halvar.at/elektronik/kleiner_bascom_avr_kurs/]Kleiner Bascom AVR Kurs[/url]
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 9. Dezember 2007, 12:18

meneliel hat geschrieben:
Leonidas hat geschrieben:Weil das Datenbankmodul nicht überall Variablen zulässt - etwa bei Tabellennamen nicht, die möchte das Datenbankmodul statisch haben.
Das kann ich ja dann aber mit Stringersetzungen umgehen. Das heißt ich komme im Prinzip eh nicht um sie herum.
An sich nicht - oder doch. Je nach Problem ist es so das Tabellennamen fest sein sollten.

Nochmal um es festzuhalten:
Die Datenstruktur _sollte_ fest sein, lediglich die Daten sollten variabel sein.

Die einzige Möglichkeit die mir einfällt wo man Abfragen verwendet die mit variablen Strukturen arbeiten sind SQL-Editoren. Da _musst_ du dich aber dann um das escapen kümmern.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Montag 10. Dezember 2007, 10:10

Leonidas hat geschrieben:An sich nicht - oder doch. Je nach Problem ist es so das Tabellennamen fest sein sollten.

Nochmal um es festzuhalten:
Die Datenstruktur _sollte_ fest sein, lediglich die Daten sollten variabel sein.

Die einzige Möglichkeit die mir einfällt wo man Abfragen verwendet die mit variablen Strukturen arbeiten sind SQL-Editoren. Da _musst_ du dich aber dann um das escapen kümmern.
Natürlich, die Datenstruktur ist fest. Variabel über User-Eingabe habe ich nur beim Erstellen der Datenbank, den Tabellennamen (kann ich zur Not weglassen), die Datenbank wird ja eh nur einmal erstellt^^ und das Passwort zur Datenbank.

Das Problem was ich jetzt habe, betrifft ja auch mehr den Where Teil der Abfragen. Und der kann ja sehr variabel sein, einmal nur ein einzelner "Wert" der eingesetzt wird, ein ander mal eine etwas längere, verknüpfte Abfrage.

@ gerold: Danke für den Tipp mit den Templates: das funktioniert prima und löst gerade alle oben genannten Probleme, zumindest soweit ich das beim Rumprobieren bisher feststellen konnte. Danke :)
meneliel
User
Beiträge: 256
Registriert: Montag 25. Juni 2007, 08:35
Kontaktdaten:

Dienstag 11. Dezember 2007, 12:10

Noch zum gleichen Problem: ein Freund hat jetzt noch ganz doll geschimpft mit mir, dass ich doch nicht einfach den User per Eingabe Teile einer Abfrage formulieren lassen kann, ohne das abzuchecken.

Durch Verwendung von Templates hab ich ja praktisch immer noch so eine SQL-Injection-Lücke, richtig? Ich glaube zwar nicht, dass da wer Code einschleußen kann, weil 1. nur der Where Teil formuliert wird und der z.T ja nicht mal komplett. WAS aber passieren kann, dass da wer irgendwas eingibt was es in der Datenbank praktisch nicht gibt, oder sich einfahc nur vertippt. Das ganze hab ich jetzt mal so abgefangen:

Code: Alles auswählen

while True:
    try:
        sel_indicatoren = Template('select NR FROM Indizes Where $where'
                                   ).substitute(where = raw_input(
                                       "Welche Indikatoren wollen Sie berechnen? (Formulieren v.'WHERE'(ohne das Schlüsselwort 'Where'):\n"))
        cursor.execute(sel_indicatoren)
        break
    except MySQLdb.OperationalError:
        print "Ihre Eingabe war nicht korrekt, bitte wiederholen.\n\n\n"
calc_cat_selection = cursor.fetchall()

while True:
    sel_ebenen = raw_input("Welche Ebenen wollen sie berechnen? Wählen Sie 1 - Bundesland, \n\
                        2-Landkreis,\n\
                        3- Regierungsbezirk,\n\
                        4- Landkreis/kreisfreie Städte:   ")
    if sel_ebenen not in ("1","2","3","4"):
        print "Ungültige Eingabe, bitte wiederholen sie diese.\n\n\n"
    else:
        break

while True:
    sel_land = raw_input("Für welches Bundesland wollen sie berechnen? (Bl-Kürzel (Bitte eingabe mit '""', bzw ALLE:    ")
    if sel_land == "ALLE":
        ebene_query = Template('select AGS, Datum, AREA, ID from ebenen where (DES = $des)'
                           ).substitute(des = ebene[sel_ebenen])
        cursor.execute(ebene_query)
        break
    elif sel_land in bl_dict.values():
        ebene_query = Template('select AGS, Datum, AREA, ID from ebenen where (DES = $des) AND Land = $land'
                           ).substitute(des = ebene[sel_ebenen], land = sel_land)
        cursor.execute(ebene_query)
        break
    else:
        print "Ungültige Eingabe. Bitte wiederholen sie diese.\n\n\n"
ebenen_selection = cursor.fetchall()
print ebenen_selection

Gibt es irgendwelche eklatanten Sicherheitslücken? Oder geht das mit dem Checken nach gültiger Eingabe eleganter?
Antworten