Richtiger Umgang mit SQL in C++ Code
-
Hallo,
ich bin mir nicht sicher ob die Kategorie C++ passt. Bitte verschieben wenn dem nicht so ist.
Beispiel:
Ich möchte SQLite in einem Programm nutzen.
Das feuern eines Statements wird wie folgt gemacht:db.query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES ('Username','Password','Salt');");Wenn ich nun statt der fixen Werte variable Werte haben möchte dann muss ich mir den String "hinfrickeln":
db.query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES ('"+Username+"','"+Password+"','Salt');");Nun für mich heißt das:
SQL Injektion WILLKOMMEN

Was mich aber noch mehr stört ist die Tatsache das ich bei größeren Abfragen ein total unübersichtlichen, Fehleranfälligen, Quelltext habe.
Ausserdem habe ich einen riesen Aufwand, weil ich jetzt manuell alle Eingaben auf SQL Injektion prüfen darf.
Frage:
Was ist hier der "richtige" Weg? Wie geht man mit SQL und C++ um?
Ich kenne noch die Lösung der stored procedures, welche mein Problem ja beheben. Auch die SQL Injektions Frage. Jedoch bietet SQLite stored procedures nicht an.Also ihr weisen Hasen. Was schlagt ihr vor? (Bitte mit Begründung)
-
Hab damit keine Erfahrung, aber trotzdem mal ein Ansatz:
//einfach zu benutzen und sicher void DataBase::insert(const string &username, const string &password){ query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES ('"+sanitize(username)+"','"+sanitize(password)+"','Salt');"); } //oder damit man das sanitize nicht vergessen kann: struct SaneString{ SaneString(const char *cs){ s = sanitize(cs); } SaneString(const string &ss){ s = sanitize(ss); } operator string& (){ return s; } operator const string& () const{ return s; } private: string s; }; string operator +(const char *cs, const SaneString &ss){ return cs + string(ss); } //drauf achten, dass alle DataBase-Funktionen außer private member query SaneStrings nehmen void DataBase::insert(const SaneString &username, const SaneString &password){ query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES ('"+username+"','"+password+"','Salt');"); }
-
prepared statement?
-
Danke für deine Antwort nwp3.
Du versuchst mit dem sanitize() also den String abzuklopfen?
Zu welcher lib gehört denn sanitize?Sollte dem so sein, finde ich ein manuelles abklopfen und der Suche nach einem escape Zeichen etwas unsicher. Es findet sich doch immer ein Weg dies zu Umgehen.
Deine Lösung behält den SQL Code weiterhin im C++ Quelltext.
-
Schau dir mal ODB - C++ an. Vielleicht ist das ja was für dich.
-
Bashar schrieb:
prepared statement?
Bingo!
Habe die API Doku nicht genau genug gelesen.
Ich denke in SQLite wäre das dann dieser Befehl?Stellt sich die Frage wie ich den SQL Code damit Auslagern kann.
Ich hätte jetzt einfach die einzelnen Abfragen in den Konstruktor einer Database Klasse aufgenommen und die ganzen strings (statements) als private deklariert.
dann nur noch den Zugriff über Methoden erlauben.Wäre dies der richtige Weg?
Laut Doku prüft der prepare Befehl jedoch nicht auf SQL Injektion.
Bleibt diese Frage also noch offen.Edith sagt:
Mit einem Bind kann man die Variablen zuweisen
-
Normalerweise arbeitet man mit Parametern. Sollte SQLite auch können.
db.query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES (:Username, :Password, 'Salt');");Den Parametern :Username und :Password musst Du dann vor Ausführung der Abfrage die entsprechenden Werte zuweisen.
Das ganze hat auch den Vorteil, dass Du die Abfrage schon in der Parameterform preparen kannst. Dadurch wird die eigentliche Ausführung deutlich schneller.
-
Ich meine die '' bei den Parametern gehören weg.
-
hustbaer schrieb:
Ich meine die '' bei den Parametern gehören weg.
Meine ich auch.

Ich hab's geändert.
-
Danke für eure Antworten.
So wie ich das jetzt sehe kann man also mit:sqlite3_prepare()und
sqlite3_bind_intprepared statements und Parameter übergeben.
Ich werde jetzt eine Wrapper Klasse schreiben, welche dann den SQL Code in den einzelnen Methoden hat.
Danke für eure Hilfe.
-
Hi,
prepared statements ist genau der richtige Ansatz. Ich würde so etwas immer weg kapseln und einen Datenbankunabhängigen Layer dazwischen schalten, wo ich die Datenbank später eventuell dann auch mal wechseln kann. Und da ich das schon lange so machen würde, habe ich das auch getan. Das Ergebnis ist Tntdb.
Das Beispielstatement:
db.query("INSERT INTO t_user(a_username,a_password,a_salt) VALUES ('Username','Password','Salt');");Wäre mit tntdb dann so (inklusive connect):
db = tntdb::connect("sqlite:mydb.db"); tntdb::Statement ins = db.prepare("INSERT INTO t_user(a_username,a_password,a_salt) VALUES (:Username, :Password, :Salt)"); ins.set("Username", Username) .set("Password", Password) .set("Salt", Salt) .execute();Da ist dann garantiert, dass keine SQL Injection passieren kann.
-
Der nächste Schritt ist ORM zu verwenden.
Wir schreiben zB quasi keine Queries und wenn es doch mal sein muss, dann sind die se weggekapselt.
-
Shade Of Mine schrieb:
Der nächste Schritt ist ORM zu verwenden.
Wir schreiben zB quasi keine Queries und wenn es doch mal sein muss, dann sind die se weggekapselt.
Macht ihr das mit Hilfe eines Frameworks? Oder tippt ihr das alles manuell ab?
-
eddi0815 schrieb:
Schau dir mal ODB - C++ an. Vielleicht ist das ja was für dich.
Das ist genau das was ich Suche

Danke!Damit kann der Thread geschlossen werden.
Ergebnis:
Ich werde ORM (ODB - C++) verwenden.