SQLite bind arguments - are they restricted to the WHERE clause only? - android

Up to now I've been trying a lot of times to bind SQLite selection arguments in queries like the best practices suggest. But it rarely worked. I'm curious about the exact reason. Now I realized that it may be caused by the sub-queries in the FROM clause. For example...
SELECT X(result) as x, Y(result) as y, Z(result) as z FROM (select Transform(MakePointZ(?, ?, 0.0, ?), ?) as result);
and the Java code is something like...
double x, y, z;
int source, target;
final String[] selectionArgs = new String[] {
String.valueOf(x),
String.valueOf(y),
String.valueOf(z),
String.valueOf(source),
String.valueOf(target) };
Cursor c = db.rawQuery(sql, selectionArgs);
Could someone confirm if using arguments outside the WHERE clause is error or explain me where the arguments are fine to be placed?
P.s. I actually use Spatialite which is an extension to SQLite but it is based on a pure and untouched SQLite.

Parameters can be used in any place where a literal value is allowed; your SQL query itself looks fine.
However, the Android database API forces you to use string parameters, so all parameter values will be seen as strings by the database.
This will make many comparisons fails, or break other operations done on these values.
Parameters are most important for strings, where formatting problems and SQL injection attacks are possible. For plain numbers, you could just insert them directly into the SQL string.
Alternatively, convert the string values back into numbers in the database:
MakePointZ(CAST(? AS REAL), CAST(? AS REAL), ...)

Could someone confirm if using arguments outside the WHERE clause is error or explain me where the arguments are fine to be placed?
? substitution works wherever a literal is valid.
In your example code, the number of ? placeholders and bind arguments don't match and that will cause an exception.

Related

Android SQLite update - why whereargs?

The Android SDK documentation for SQLite provides an update method which takes as its parameters four values - table, values, whereClause, whereArgs. The first three make complete sense. However, it is not clear to me that using whereArgs with a whereClause containing ?'s as opposed to sending out a fully prepared whereClause offers any benefits - either in terms of security (there is no suggestion that this somehow helps to sanitize the SQL) or speed. So what then are the benefifts of going down that route instead of simply passing a full where string and a null whereArgs?
The docs say:
String: You may include ?s in the where clause, which will be replaced by the values from whereArgs. The values will be bound as Strings.
This is slightly misleading. No "replacement" takes place actually. Instead the ?s are variables and the whereArgs are values that are bound to those variables, and this binding happens inside the sqlite SQL program.
Using variable binding avoids issues such as SQL injection without the need to sanitize inputs.
Similar mechanism would be beneficial for performance in case you were executing the same SQL program over and over again with different values for variables. You only need to compile the SQL once. Android SQLite mechanism for that is SQLiteStatement (see the bind...() methods in its SQLiteProgram superclass).
Security is definitely an issue. If you use string concatenation, you are vulnerable to SQL Injection. Using ? and whereArgs does indeed sanitize the input so you are safe.
There is also the case of prepared statements - you compile them only once and then bind different values for each arguments placeholder. This will give you a benefit in terms of performance. You can't get that with your approach.

Improper Neutralization of Special Elements used in an SQL Command

My App data managed by the Content Provider using CursorLoaders is in SQLite database.
According to Veracode Static Scan report , it is prone to SQL Injections.
But according to docs,
To avoid this problem, use a selection clause that uses ? as a replaceable parameter and a separate array of selection arguments. When you do this, the user input is bound directly to the query rather than being interpreted as part of an SQL statement. Because it's not treated as SQL, the user input can't inject malicious SQL.
public Loader<Cursor> onCreateLoader(int id, Bundle b) {
return new CursorLoader(getActivity(),
NewsFeedTable.CONTENT_URI,
NewsFeedTable.PROJECTION,
"_id = ?",
new String[]{tid},
null);
}
As shown in above code, I am doing in similar way.
Also I read same in The Mobile Application Hacker's Book
If this is not sufficient measure to prevent SQL injections, how do I sanitize the sql query from the special characters?
Every read suggests using parameterized PreparedStatements.
Is it not default with Content Providers?
An alternative to SQLiteStatement is to use the query, insert, update, and delete methods on SQLiteDatabase as they offer parameterized statements via their use of string arrays.
I found this as a solution :
But then I read docs from here that
StringEscapeUtils.escapeSql
This was a misleading method, only handling the simplest of possible SQL cases. As SQL is not Lang's focus, it didn't make sense to maintain this method.
Adding the code snippet. Report points at Line 307 where SQL Injection flaw is detected:
How should I do input validation for the special characters?
Please help, to make me understand it better.
Values in selectionArgs parameters do not need to be escaped, and they must not be escaped because the escape characters would end up in the database.
There are three different cases of SQL code seen by Veracode:
values that cannot be user input (such as string literals in the source code);
values that are user input (because the come directly from, e.g., some edit box);
values that might be user input, because the tool cannot determine the source.
For marketing reasons, paid-for tools tend to inflate the problem numbers as much as possible. So Veracode reports all instances of the third case as problems.
In this case, Veracode does not know where selection comes from, so it complains. If that value is constructed by your program and never contains any user input (i.e., all user-input values are moved to ? parameters), then this is a false positive, and you must tell Veracode to shut up.

Possible to use ContentValues.put to update one column as sum of others

Is it possible to use ContentValues.put() to update a column in a SQLiteDatabse to be the sum of other columns?
I have searched on here and on the web and the closest answer I have found is: Update one column as sum of other two columns. This doesn't quite answer my question because it requires a raw database command, which I would prefer to avoid (if possible).
I have a fairly static database that I have generated unique permutations in (long setup, fast queries). I am attempting to set a total column at the end for even faster sorting on the permutations. I am currently attempting to use:
ContentValues values = new ContentValues();
values.put(totalVal, sumString);
where I have tried to set sumString to both:
=val_1+val_2+val_3...
and
val_1+val_2+val3...
When I look at my database in adb shell, sqlite3 I see:
Which looks... correct? Except when I query my database after this has been set, I get this in the log:
My val_* columns show values in the same adb shell, sqlite3 dump. Also, I do not set the totalVal column to this sumString until the val_* columns are all populated with their values.
Is it just not possible to use ContentValues.put()? Does it do some sort of internal escaping?
The reason it seems like it should work to me is the totalVal column is set to REAL so if ContentValues.put() does do internal escaping I thought I would get an error since I would essentially be putting a String value in a column that should only accept REAL.
Like I said earlier, my database is pretty static and only there for fast queries and sorting. It would be possible for me to loop through all the val_* columns and manually sum them up. Although, there are thousands of rows in my database so I was hoping to avoid this and was looking for a more elegant way to do this.
Thanks for the help.
SQLiteDatabase.update() is just a convenience method for updating rows in the database, so in your case you are just overcomplicating things trying to use ContentValues instead of SQLiteStatement and binding arguments which is what SQLiteDatabase.update() uses internally but preventing that column names were considered Strings.
It's not very clear from your example but if you are trying to update some values and at the same time calculate the totalVal do something like this
SQLiteStatement stmt = db.compileStatement("UPDATE mytable SET val_1=?, val_2=?, val_3=?, totalVal=val_1+val_2+val_3 WHERE expr");
stmt.bindLong(1, 1);
stmt.bindLong(2, 3);
stmt.bindLong(3, 5);
stmt.executeUpdateDelete();
EDIT
So as mentioned in your comment you don't need to bind values, it's even simpler
final int rows = db.compileStatement("UPDATE mytable SET totalVal=val_1+val_2+val_3").executeUpdateDelete();
and regarding your comment about "raw" SQL, ContentValues are not an option so this is the only way (AFAIK).

Android - passing field name as selectionArgs to rawQuery

I'm passing the below statement as a rawQuery in Android:
SELECT DISTINCT ltUsers._id,ltUsers.NAME,ltUsers.GLOBAL_ID, ltGroups.GROUP_NAME
FROM ltUsers
JOIN ltGroups ON (ltUsers.GROUP_ID = ltGroups.GLOBAL_ID)
WHERE ltgroups.GLOBAL_ID = ? " +
ORDER BY ltUsers.NAME ASC,ltgroups.GLOBAL_ID ASC;
With the rawQuery as follows:
Cursor c = db.rawQuery(sql,args)
It works just fine if I pass a value to the parameter, e.g.
String[] args = new String[]{"2"}
However, I also want to be able to show all rows, unlimited by the GLOBAL_ID in the WHERE clause. Testing on a dump of my SQLite database outside of Android - as well as in Android by just writing the parameter directly into the statement - shows the following clause to be a valid way to do this:
WHERE ltGroups.GLOBAL_ID = ltGroups.GLOBAL_ID
Yet when I pass the field reference ltGroups.GLOBAL_ID or [ltGroups].[GLOBAL_ID] as a parameter it fails to return any rows in the rawQuery. Any ideas on why this might be happening? Happy to produce any extra information.
Parameters always replace specific values, not anything else.
When you put the string "ltGroups.GLOBAL_ID" into the parameters array, it is interpreted as exactly that, a string.
(To show all records, just omit the WHERE clause.)

Why do some SQLite methods expect arguments as an Object[] while others need a String[]?

I'm curious about the design of the Android SQLite API. For example, we have
public void execSQL(String sql, Object[] bindArgs)
for SQLs that don't return data, and we also have
public Cursor rawQuery(String sql, String[] selectionArgs)
for SQLs that do return data.
Why are the arguments expected as an Object array in the former and as a String array in the latter case? I understand that accepting only Strings might make sense due to SQLite's type affinity. However, if that is the case, why not consistently use String arrays for arguments? Is there some non-String argument that makes sense for execSQL but not for rawQuery?
Actually, accepting only strings does not make sense because strings never compare equal with numbers, i.e., queries like
c.rawQuery("SELECT * FROM mytable WHERE some_number_column = ?", args);
will never return any records with numbers. (You would have to declare all columns as TEXT, and never use expressions or convert them with something like CAST(... AS TEXT).)
Jens mentioned a possible explanation, but that is no excuse for inconsistent and plain bad design.
There is one. For example if you want to compare BLOB values. Have a look at this example.
UUID stored as BLOB
db.execSQL("DELETE FROM "+USER_TABLE+" WHERE "+USER_UUID+"=?",
new Object [] { uuid.toByteArray }
According to documentation, byte[], String, Long and Double are supported in bindArgs. However, it is not recommended to use this method for executing SELECT/INSERT/UPDATE/DELETE statements.
I just don't know why.

Categories

Resources