2012-03-25 203 views
12

在Android中,android.database.sqlite.SQLiteStatement允许我在SQLite中使用预准备语句来避免注入攻击。它的execute方法适用于创建/更新/删除操作,但是对于返回游标等的查询似乎没有任何方法。查询与Android中的准备语句?

现在在iOS中,我可以创建类型为sqlite3_stmt*的预准备语句并将它们用于查询,所以我知道这不是SQLite的限制。我如何使用Android中的预准备语句执行查询?

回答

39

准备好的语句可以做两件事情

  • 加速性能,因为数据库不需要每次解析声明
  • 绑定&逃生参数在声明中让你节省反对注入攻击

我不知道确切位置/时,机器人会SQLite的实现实际上使用sqlite3_prepare(afiak不sqlite3_prepare_v2 - 见here),但我吨使用它,否则你不能得到Reached MAX size for compiled-sql statement cache errors

所以,如果你想查询数据库,你必须依赖于实现,我不知道用SQLiteStatement做到这一点。

关于注射安全性,每个数据库查询,插入等方法都有(有时候是可选的)版本,允许您绑定参数。

E.g.如果你想获得一个Cursor

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

Cursor SQLiteDatabase#rawQuery(

  • String sql,:全SELECT statment可以包括?到处
  • String[] selectionArgs:即取代?值列表,在他们出现的顺序

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

Cursor SQLiteDatabase#query (

  • String table,:表名,可以包括JOIN
  • String[] columns,:所需的列的列表中,null = *
  • String selection,:WHERE条款withouth的WHERE可/应包括?
  • String[] selectionArgs,:GROUP BY条款W/O GROUP BY
  • String having,:HAVING条款,替换?,为了值出现
  • String groupBy,名单W/O HAVING
  • String orderByORDER BY子句的w/o ORDER BY

)

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

通过ContentProviders - 因为你有一个抽象的提供商,而不是一个数据库进行交互这种情况下略有不同。我们不保证有一个支持ContentProvider的sqlite数据库。所以,除非你知道哪些列/哪些提供者在内部工作,你应该坚持文档所说的。

Cursor ContentResolver#query(

  • Uri uri,:一个URI表示该数据源(内部转换到一个表)
  • String[] projection,:所需的列的列表中,null = *
  • String selection,:WHERE子句withouth WHERE can/should include ?
  • String[] selectionArgs,:ORDER BY条款W/O ORDER BY

)

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

提示:该更换?,以便它们出现

  • String sortOrder值的列表,如果你想LIMIT在这里你可以添加它的ORDER BY条款:

    String sortOrder = "somecolumn LIMIT 5"; 
    

    或取决于ContentProvider执行添加它作为参数传递给Uri

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

    在所有情况下?将通过你把在绑定参数中的转义版本所取代。

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

  • +1

    优秀帖子,谢谢你,帮了我很多。像这样的时代让我觉得我们需要能够不止一次地改装。 – Louis 2012-07-03 07:11:18

    +1

    即使这个答案很安静,是否有一种解决方法来绑定除String以外的其他任何东西。我的查询请求一个数值被绑定,但SqliteDatabase中的每个方法都使用一个String数组。如果我自己创建PreparedStatement,我可以执行查询来恢复光标......我不相信没有解决方案,但我一直没有找到它。 – AxelH 2015-08-25 07:06:18

    +0

    @AxelH解决方法是,所有东西都可以用字符串表示。例如使用'String.valueOf(yourNumericThingHere)'。 – zapl 2015-08-25 07:27:41