diff options
author | Daniel Lehmann <lehmannd@google.com> | 2011-06-01 15:24:23 -0700 |
---|---|---|
committer | Daniel Lehmann <lehmannd@google.com> | 2011-06-01 15:24:23 -0700 |
commit | 50b1f8d3fc1379339119933e8f567547efb89aa5 (patch) | |
tree | 72dbf5ed4b78ac85de1f619fdcbf6d4a8fc28a03 /core/java/android/database | |
parent | 2978cef0a77550ea3a364ffbf42fc43f2029070e (diff) | |
download | frameworks_base-50b1f8d3fc1379339119933e8f567547efb89aa5.zip frameworks_base-50b1f8d3fc1379339119933e8f567547efb89aa5.tar.gz frameworks_base-50b1f8d3fc1379339119933e8f567547efb89aa5.tar.bz2 |
Enable strict mode as a public api to catch sql injections
Bug: 4368912
Change-Id: Ia4919f58cc5264da8758d6cd61d93e031676b74a
Diffstat (limited to 'core/java/android/database')
-rw-r--r-- | core/java/android/database/sqlite/SQLiteQueryBuilder.java | 70 |
1 files changed, 58 insertions, 12 deletions
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index b6aca2b..9c09e81 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -24,8 +24,8 @@ import android.util.Log; import java.util.Iterator; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import java.util.regex.Pattern; /** @@ -43,7 +43,7 @@ public class SQLiteQueryBuilder private StringBuilder mWhereClause = null; // lazily created private boolean mDistinct; private SQLiteDatabase.CursorFactory mFactory; - private boolean mStrictProjectionMap; + private boolean mStrict; public SQLiteQueryBuilder() { mDistinct = false; @@ -145,10 +145,37 @@ public class SQLiteQueryBuilder } /** + * Need to keep this to not break the build until ContactsProvider2 has been changed to + * use the new API + * TODO: Remove this * @hide */ public void setStrictProjectionMap(boolean flag) { - mStrictProjectionMap = flag; + } + + /** + * When set, the selection is verified against malicious arguments. + * When using this class to create a statement using + * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)}, + * non-numeric limits will raise an exception. If a projection map is specified, fields + * not in that map will be ignored. + * If this class is used to execute the statement directly using + * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)} + * or + * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)}, + * additionally also parenthesis escaping selection are caught. + * + * To summarize: To get maximum protection against malicious third party apps (for example + * content provider consumers), make sure to do the following: + * <ul> + * <li>Set this value to true</li> + * <li>Use a projection map</li> + * <li>Use one of the query overloads instead of getting the statement as a sql string</li> + * </ul> + * By default, this value is false. + */ + public void setStrict(boolean flag) { + mStrict = flag; } /** @@ -217,13 +244,6 @@ public class SQLiteQueryBuilder } } - private static void appendClauseEscapeClause(StringBuilder s, String name, String clause) { - if (!TextUtils.isEmpty(clause)) { - s.append(name); - DatabaseUtils.appendEscapedSQLString(s, clause); - } - } - /** * Add the names that are non-null in columns to s, separating * them with commas. @@ -320,6 +340,19 @@ public class SQLiteQueryBuilder return null; } + if (mStrict && selection != null && selection.length() > 0) { + // Validate the user-supplied selection to detect syntactic anomalies + // in the selection string that could indicate a SQL injection attempt. + // The idea is to ensure that the selection clause is a valid SQL expression + // by compiling it twice: once wrapped in parentheses and once as + // originally specified. An attacker cannot create an expression that + // would escape the SQL expression while maintaining balanced parentheses + // in both the wrapped and original forms. + String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy, + having, sortOrder, limit); + validateSql(db, sqlForValidation); // will throw if query is invalid + } + String sql = buildQuery( projectionIn, selection, groupBy, having, sortOrder, limit); @@ -329,7 +362,20 @@ public class SQLiteQueryBuilder } return db.rawQueryWithFactory( mFactory, sql, selectionArgs, - SQLiteDatabase.findEditTable(mTables)); + SQLiteDatabase.findEditTable(mTables)); // will throw if query is invalid + } + + /** + * Verifies that a SQL statement is valid by compiling it. + * If the SQL statement is not valid, this method will throw a {@link SQLiteException}. + */ + private void validateSql(SQLiteDatabase db, String sql) { + db.lock(sql); + try { + new SQLiteCompiledSql(db, sql).releaseSqlStatement(); + } finally { + db.unlock(); + } } /** @@ -541,7 +587,7 @@ public class SQLiteQueryBuilder continue; } - if (!mStrictProjectionMap && + if (!mStrict && ( userColumn.contains(" AS ") || userColumn.contains(" as "))) { /* A column alias already exist */ projection[i] = userColumn; |