A lot has
been talked about the threat of SQL Injection attack for web applications but we
don’t find many instances that point to the relevance of this attack in the
case of mobile applications.
Aren't mobile applications prone to this attack?
The answer
is yes, in this article we will see why SQL Injection flaw manifests in android
applications and how can we mitigate it.
Let’s look
at hybrid and browser-based applications first. A hybrid android application that
loads a web application in its “webview” faces the threat of SQL injection if
the application that it loads is vulnerable to it.
Consider a similar
hybrid android application that loads a web portal as shown below. On launching
the application it displays a web login page.
Now, if a
SQL Injection flaw exists on this web page we can exploit it using this mobile interface
in a similar manner as we would do it using a web browser.
Let’s see
how.
We will try to
enter a SQL Injection attack vector i.e. “‘ OR ‘1’ = ‘1’ #”
and attempt to bypass the authentication. Here a true condition is used
instead of a valid login ID in-order to manipulate the backend query and succeed
it.
Woo! Observe
that the attack vector succeeds and a valid login is obtained without valid
credentials, as depicted in the screenshot below.
This means
that the server side code that handles the login request has a SQL Injection
vulnerability.
The case
that we have seen here illustrates a server
side SQL injection flaw that was exploited on a mobile interface. Similar
flaws can exist in many “hybrid” and “browser-based” mobile application that
loads the web application. This is server
side SQL injection flaw.
Is this the only kind of SQL Injection flaw applicable
to mobile applications?
No, there
could be instances where even the native code is vulnerable to this attack. We will
see the case of Android applications here.
Android
application uses a light weight SQLite database and an extensive set of API
provided by android SDK to process it.
A classic
example of the use of SQLite database by android applications is in the case of
local authentication.
Consider a
case of an android application that stores the user data locally on the device and
implements client-side authentication logic. Though local authentication is an insecure design in itself it has been
used in this example just for the sake of illustrating the SQL Injection
attack.
On launching
the application it throws up a native login screen:
On
successful login it takes the user to the next screen, as shown below.
And in case
the login is invalid it disallows access to the next screen and alerts the
user.
Now,
consider bypassing this local authentication using SQL Injection technique?
Let’s
attempt it. We will use the same attack vector that we used in the case of the
server side flaw i.e. “ ‘ OR
1=1 --”
Bingo!! The
application grants access to the next screen, as shown in the below screenshot.
This means that the attack vector succeeded and the application got hacked.
This is client side SQL Injection flaw.
Let’s
understand it in more detail.
Why did the attack occur in general?
The root
cause of SQL Injection vulnerability is use of dynamic or concatenated SQL
queries. If the SQL queries are constructed by concatenating user supplied
inputs it leads to this flaw. The user can then supply SQL attack vectors
instead of valid inputs and manipulate the backend SQL query.
What went wrong in that Android application?
Let’s look
at vulnerable piece of code that led to the attack.
The
authentication logic of the application is written within the “check_login” method, which fires a SQL
query to authenticate the user. And the flaw lies in the way that SQL query is
formed and executed.
We will see
3 different instances of the “check_login”
method in order to get familiar with most of the insecure implementations that
can be present in Android applications.
Insecure implementation 1: Vulnerable use of “rawQuery” method
In this
implementation the “getReadableDatabase”
method returns an instance of SQLiteDatabase class. And then its “rawQuery” method is used to execute the
login SQL query as shown below.
Observe that
the SQL query is formed in a concatenated fashion with the values of the
username and password fields and the resultant string is passed as an argument
to the “rawQuery” method.
As the user inputs are concatenated with the login query the SQL attack vector sent by an attacker as an input (“OR 1=1” in our case) is able to modify the backend query and yield desired results. Thus, we could bypass login authentication using a true condition.
Similarly, there
is one more method of “SQLiteDatabase”
class that is often use to execute SQL queries called “execSQL” that takes SQL string as an input and
executes it. However it does not result any cursor values unlike “rawQuery”. Use of “execSQL” to run concatenated SQL queries will also lead to this
flaw.
Mitigation:
The only fool
proof way of preventing SQL Injection is the use of parameterized queries i.e.
instead of using concatenated SQL queries we must use parameterized SQL queries.
The parameterized queries are formed using place holders (“?”) in the place of
input values wherever they are expected in the query. These place holders are
then bound with the corresponding user inputs while processing it. This way the
SQL query is not influenced by the input and SQL injection attack is mitigated.
But, does android provide any such API to write
parameterized queries?
Yes,
definitely. Observe that “rawQuery”
that takes 2 arguments. The first argument takes the SQL query and the second argument
is used to supply the input values that must be bound to the place holders
present in the query, if the query supplied is parameterized as shown below.
So, in order
to prevent SQL Injection we must write the SQL queries in a parameterized way.
And in the
case of “execSQL” an overloaded method is present that takes 2 arguments, one for
the query and other for the bind variables.
Make use
appropriate “rawQuery” and “execSQL” methods that take bind
values as input, as illustrated above.
Insecure Implementation 2: Use of concatenated SQL queries in the
selection criteria.
There are
some methods provided by Android APIs that offer a different way of querying
the database. One of such instances is the "query" method of "SQLiteDatabase" class. Here, the table to be queries, the
columns to be returned and the condition used to fetch the data (i.e. where
clause condition) are taken as separate arguments. Here, even if a complete SQL
query is not taken as a single argument but the chances of SQL Injection
vulnerability exists. The developers usually make a mistake of adding a
concatenated string in the argument related to the where clause condition leading
to a SQL Injection hole as shown below.
Mitigation:
Observe the
method invocation snippet shown above carefully and note that the argument next
to the where clause condition is set to null. However, this argument is
supposed to take bind variables value in case the condition supplied is
parameterized. Though the API provides a mechanism to use parameterized
conditions but it does not enforce it.
Use parameterized where clause condition
and pass the bind variables separately while using the "query"
and all
such methods like - update, delete, etc. that have similar
implementations.
Insecure Implementation 3: Use of SQLStatement
This is an
alternate way of executing SQL queries, here an instance of “SQLiteStatement”
class is used to execute the SQL query. The SQL string is passed as an argument
to "compileStatement" method which returns the instance of
the SQLiteStatement class.
The SQL
query is then executed by calling the execute method of the statement class.
As the “compileStatement”
method takes a single argument, developers often supply a concatenated SQL
query to it creating a hole for this vulnerability, as shown below.
Mitigation:
Even though “compileStatement” method takes only one argument, a parameterized query can be passed to it. And later the bind variable values can be bound to the inputs with the help of the “bindString” method.
Even though “compileStatement” method takes only one argument, a parameterized query can be passed to it. And later the bind variable values can be bound to the inputs with the help of the “bindString” method.
As a thump rule always read the description
of the method carefully given in the Android developer guide before using them.
Most of the methods that are used to write SQL queries give an option to write
parameterized queries and add bind variables. But they don’t enforce it. So be
cautious about SQL Injection, use only parameterized SQL queries at all the
places in the application.