summaryrefslogtreecommitdiffstats
path: root/core/java/android/database
diff options
context:
space:
mode:
authorDaniel Lehmann <lehmannd@google.com>2011-06-01 15:24:23 -0700
committerDaniel Lehmann <lehmannd@google.com>2011-06-01 15:24:23 -0700
commit50b1f8d3fc1379339119933e8f567547efb89aa5 (patch)
tree72dbf5ed4b78ac85de1f619fdcbf6d4a8fc28a03 /core/java/android/database
parent2978cef0a77550ea3a364ffbf42fc43f2029070e (diff)
downloadframeworks_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.java70
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;