- Android Database Security Issues
- SQLite
- SQLCipher
- Hiding the Key
- SQL Injection
- Conclusion
SQL Injection
SQL injection refers to when the attacker taints the data with a SQL statement. We said earlier that SQLite is a fully functional database, so, just like your SQL Server or MySQL box, it is just as susceptible to SQL injection if you are not careful. SQL injection typically works by adding data to the querystring or adding data in a form field to give the hacker access to the database or unauthorized logins. And while SQL injection is usually something used for attacking a web view or a web service, it can also be an attack on an Activity. Figure 5-11 shows a simple SQL injection example.
Figure 5-11 Classic SQL injection attack
If we look at the checkLogin code in Listing 5-12 we can see that the SQL query is passed directly to the database. So if we log in with a username of ’ OR 1=1 --’ and password of test, the query to SQLite will be the following string:
select * from login where USERNAME = '' OR 1=1 --' and PASSWORD = 'test'
Listing 5-12 Login unprotected from SQL injection
public boolean checkLogin(String param1, String param2) { boolean bool = false; Cursor cursor = db.rawQuery("select * from login where USERNAME = '" + // line 5 param1 + "' and PASSWORD = '" + param2 + "';", null); if (cursor != null) { if (cursor.moveToFirst()) bool = true; cursor.close(); } return bool; }
Because of the OR 1=1 portion of the string and the --, which comments out the rest of the SQL query, this will always be a true condition. The result is that the user can log in without needing a real username and password.
To fix this we need to sanitize any user-entered data and assume it can’t be trusted. We can do this either by using regular expressions to check that it’s what we’re expecting—for example, a valid email address—or by using SQL prepared statements. Or better still, we can do both.
To fix our checkLogin code we’re going to change the SQL to use prepared statements. Listing 5-13 shows a modified checkLogin, which now uses prepared statements on line 5. Here the injected SQL becomes a parameter and can no longer cut off the SQL statement.
Listing 5-13 Protecting code using prepared statements
public boolean checkSecureLogin(String param1, String param2) { boolean bool = false; Cursor cursor = db.rawQuery("select * from login where " + // line 5 "USERNAME = ? and PASSWORD = ?", new String[]{param1, param2}); if (cursor != null) { if (cursor.moveToFirst()) bool = true; cursor.close(); } return bool; }