2012-03-25 6 views
12

In Android, android.database.sqlite.SQLiteStatement ermöglicht es mir, vorbereitete Anweisungen in SQLite zu verwenden, um Injektionsangriffe zu vermeiden. Seine Methode execute eignet sich für Vorgänge zum Erstellen/Aktualisieren/Löschen, aber es scheint keine Methode für Abfragen zu geben, die einen Cursor oder Ähnliches zurückgibt.Abfragen mit vorbereiteten Anweisungen in Android?

Jetzt in iOS kann ich vorbereitete Anweisungen des Typs sqlite3_stmt* erstellen und sie für Abfragen verwenden, so dass ich weiß, dass dies keine Einschränkung von SQLite ist. Wie kann ich Abfragen mit vorbereiteten Anweisungen in Android durchführen?

Antwort

39

eine vorbereitete Anweisung ermöglicht es Ihnen, zwei Dinge

  • beschleunigen die Leistung zu tun, da die Datenbank nicht die Aussage jedes Mal
  • binden & Flucht Argumente in der Anweisung zu analysieren, benötigt also sind Sie speichern gegen Injection-Angriffe

ich weiß nicht genau, wo/wann Androids SQLite Implementierung sqlite3_prepare tatsächlich verwendet (afiak nicht sqlite3_prepare_v2 - siehe here), aber ich t verwendet es sonst, Sie könnten Reached MAX size for compiled-sql statement cache errors nicht erhalten.

Also, wenn Sie die Datenbank abfragen wollen, müssen Sie auf die Implementierung verlassen, es gibt keine Möglichkeit, es mit SQLiteStatement zu tun.

In Bezug auf die Injektionssicherheit hat jede Datenbankabfrage-, insert-, usw.-Methode (manchmal alternative) Versionen, mit denen Sie Argumente binden können.

z.wenn Sie wollen ein Cursor aus

SELECT * FROM table WHERE column1='value1' OR column2='value2' 

Cursor SQLiteDatabase#rawQuery(

  • String sql erhalten: Voll SELECT statment die einschließen können ? überall
  • String[] selectionArgs: Liste von Werten, die ? ersetzen, in Reihenfolge sie erscheinen

)

Cursor c1 = db.rawQuery(
    "SELECT * FROM table WHERE column1=? OR column2=?", 
    new String[] {"value1", "value2"} 
); 

Cursor SQLiteDatabase#query (

  • String table,: Tabellenname, können umfassen JOIN etc
  • String[] columns,: Liste der Spalten erforderlich, null = *
  • String selection,: WHERE Klausel withouth WHERE kann/sollte ?
  • String[] selectionArgs, umfassen: Liste von Werten, die ?, ersetzen, um sie erscheinen
  • String groupBy,: GROUP BY Klausel w/o GROUP BY
  • String having,: HAVING Klausel w/o HAVING
  • String orderBy: ORDER BY Klausel w/o ORDER BY

)

Cursor c2 = db.query("table", null, 
    "column1=? OR column2=?", 
     new String[] {"value1", "value2"}, 
     null, null, null); 

Via ContentProviders - dieser Fall ist etwas anders, da Sie mit einem abstrakten Anbieter interagieren, keine Datenbank. Es gibt keine Garantie, dass es eine SQLite-Datenbank gibt, die die ContentProvider unterstützt. Wenn Sie also nicht wissen, welche Spalten es gibt/wie der Provider intern arbeitet, sollten Sie sich an die Dokumentation halten.

Cursor ContentResolver#query(

  • Uri uri,: eine URI, die die Datenquelle (intern in einer Tabelle übersetzt) ​​
  • String[] projection, Liste der Spalten erforderlich, null = *
  • String selection,: WHERE Klausel ohne WHERE kann/sollte enthalten ?
  • String[] selectionArgs,: Liste von Werten, die ? ersetzen, um sie
  • String sortOrder erscheinen: ORDER BY Klausel w/o ORDER BY

)

Cursor c3 = getContentResolver().query(
    Uri.parse("content://provider/table"), null, 
    "column=? OR column2=?", 
     new String[] {"value1", "value2"}, 
     null); 

Hinweis: Wenn Sie auf LIMIT hier möchten, können Sie hinzufügen es zu der ORDER BY Klausel:

String sortOrder = "somecolumn LIMIT 5"; 

oder abhängig von der Implementierung der ContentProvider es als Parameter an die Uri hinzufügen:

Uri.parse("content://provider/table?limit=5"); 
// or better via buildUpon() 
Uri audio = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 
audio.buildUpon().appendQueryParameter("limit", "5"); 

In allen Fällen ? wird durch die maskierte Version von ersetzt werden, was Sie in dem bind Argumente setzen.

? + "hack'me" = 'hack''me'

+1

Outstanding Post, danke, half mir sehr viel. Zeiten wie diese lassen mich denken, dass wir in der Lage sein müssen, mehr als einmal zu modeln. – Louis

+1

Auch wenn diese Antwort ruhig alt ist, gibt es einen Workaround, um irgendetwas anderes als einen String zu binden. Meine Abfragen fordern einen numerischen Wert als Bindung an, aber alle Methoden von SqliteDatabase verwenden ein String-Array. Und wenn ich das PreparedStatement selbst erstelle, kann ich die Abfrage ausführen, um den Cursor wiederherzustellen ... Ich kann nicht glauben, dass es keine Lösung gibt, aber ich habe es nicht gefunden. – AxelH

+0

@AxelH Die Lösung ist, dass alles als String ausgedrückt werden kann. Verwenden Sie zum Beispiel 'String.valueOf (yourNumericThingHere)'. – zapl

Verwandte Themen