Android application is already developed using ActiveAndroid
public static List<ModelNames> search(String pattern) {
return new Select().from(ModelNames.class)
.where("title LIKE '%" + pattern + "%' or content LIKE '%" + pattern + "%'")
.orderBy("title")
.execute();
}
Now its prone to SQL injections.
Has anyone faced a similar problem and found a solution or could anyone provide a solution for the same?
Found a issue on github, but could not get a proper solution.
The examples on the website show how to use placeholders:
public static List<ModelNames> search(String pattern) {
pattern = "%" + pattern + "%";
return new Select().from(ModelNames.class)
.where("title LIKE ? or content LIKE ?", pattern, pattern)
.orderBy("title")
.execute();
}
What I do is assuming, that everything a user inputs is a threat, so I would save everything to variables like usUsername, where "us" means unsafe. After that I check every "us"-variable for injections, what results in sUsername (s means safe). So when I build a query I can only use s-varaibles and should be safe in most cases.
This idea is totally taken from here: http://www.joelonsoftware.com/articles/Wrong.html
Already mentioned in this question.
I found out a workaround, But I let other people to answer with better proposal with respect ActiveAndroid ORM.
following is the workaround:
public static List<ModelNames> search(String pattern) {
return new Select().from(ModelNames.class)
String pattern = DatabaseUtils.sqlEscapeString(searchBar.getText().toString());
pattern = pattern.substring(1, pattern.length());
pattern = pattern.substring(0, pattern.length()-1);
.where("title LIKE '%" + pattern + "%' or content LIKE '%" + pattern + "%'")
.orderBy("title")
.execute();
}
Ref: sqlEscapeString
Related
I have been able to achieve a basic search capability in Android Room + FTS with the following query as an example in my Dao:
#Query("SELECT * FROM Conversation JOIN ConversationFts ON Conversation.id == ConversationFts.id WHERE ConversationFts.title LIKE :text GROUP BY Conversation.id")
public abstract DataSource.Factory<Integer, Conversation> search(String text);
Where the text is passed along between percentage characters, as such %lorem%.
This example works perfectly for the search of a single word and I want to expand this to be able to search for one or more words with the condition that they do not need to be in the order they are entered, but they must have a match for all the words. This means this has to be an AND and not OR situation.
I've found a few examples of SQL queries, but they require a specific query tailored for each case, for example: LIKE text AND LIKE another AND LIKE, etc... which is not a solution.
How can this be achieved? And to make it clear, I am not looking for a raw query solution, I want to stick to Room as much as possible, otherwise, I'd rather just use this as it is than to resort to raw queries.
EDIT: Adding an example per request
I search for do the results returned include all matches that contain do in the title, even if it is a partial match
I search for do and test the results returned include all matches that contain do and test, but in no specific order, even if they are partial matches.
However, if just one of them cannot be found in the text then it will not be returned in the results. For example, if do is found, but test is not then it will not be part of the results.
I think there is no way to do that except creating a raw query dynamically. You can write another method, something like this:
public abstract class ConversationDao {
public DataSource.Factory<Integer, Conversation> search(String text) {
StringBuilder builder = new StringBuilder();
String[] words = text.split("\\s+");
if (words.length > 0) {
builder.append(" WHERE ");
}
for (int i = 0; i < words.length; i++) {
builder.append("ConversationFts.title LIKE %").append(words[i]).append("%");
if (i < words.length - 1) {
builder.append(" AND ");
}
}
SupportSQLiteQuery query = new SimpleSQLiteQuery(
"SELECT * FROM Conversation JOIN ConversationFts ON Conversation.id == ConversationFts.id"
+ builder.toString()
+ " GROUP BY Conversation.id"
);
return search(query);
}
#RawQuery
public abstract DataSource.Factory<Integer, Conversation> search(SupportSQLiteQuery query);
}
I'm trying to do some Sqlite querying but I don't know if I'm doing it correctly because this feels really unsave to do. So my question is how do I fix this. I'm new to the whole Xamarin and Sqlite usage.
I'm only making a Android project so it is not a cross platform application. I also cant seem to figure out where to get Mono.Data.Sqlite if I even need it. Everything is welcome.
static public List<Users> SelectUser(string name)
{
try
{
var dbConn = new SQLiteConnection(DatabasePath);
{
return dbConn.Query<Users>("SELECT name, email FROM TblUsers where name = " + name+ ";");
}
}
catch (SQLiteException ex)
{
return null;
}
}
You should use Prepared Statements.
There is an official java documentation about Prepared Statements from Oracle here.
You can also search it on google. There are a lot of guides on how to use prepared statements.
I'm working on an android app that uses sugarORM. I want to get a multiple items that match the ids in a list.
However when i call
findWithQuery(A.class, "SELECT * FROM <table> WHERE <column> in (?)", "1,2,3")
I always get an empty list(although I double checked the query with SQLite DB Browser and it worked).
Splitting this query into multiple findById seems inefficient. Any thoughts on getting WHERE IN to work using SugarORM?
After more attempts I found that there is a problem with replacing the placeholders.
Switching from:
findWithQuery(A.class, "SELECT * FROM <table> WHERE <column> in (?)", "1,2,3")
To:
findWithQuery(A.class, "SELECT * FROM <table> WHERE <column> in (1,2,3)", null)
fixes the issue.
The issue is that SQLite escapes the arguments "1,2,3" and turns it into a single value that is then used to replace the single ? placeholder in your query string. The correct way to supply multiple arguments would be to have a placeholder for every individual argument. Your original line of code would then have to change to:
findWithQuery(A.class, "SELECT * FROM <table> WHERE <column> in (?,?,?)", new String[] { "1","2","3" })
You later pointed out that the number of arguments is dynamic. This you can easily be accomplished by generating the query (the where clause in particular) at runtime based on the arguments that you want to query for.
For the most general case, it only takes a few lines of code to do so:
final String[] args = new String[] { /* ... */ };
final String query = "SELECT * FROM <table> WHERE <column> in " +
"(" + TextUtils.join(",", Collections.nCopies(args.length, "?")) + ")";
final List<A> result = A.findWithQuery(A.class, query, args);
(note that you could take a shortcut and inject the arguments directly into the query string - instead of using placeholders - but then you'll loose SQLite's built-in escaping, so I decided against that)
All that's left to do is to generate a String[] out of your arguments. A simple helper method like this should cover most scenarios:
static String[] toStringArray(Object... args) {
final String[] array = new String[args.length];
for (int i = 0; i < args.length; i++) array[i] = args[i].toString();
return array;
}
You'll probably want to add some null checks in there and potentially set up a few overloads if you plan on using primitive arrays as arguments.
Disclaimer: I typed everything straight into the browser, so no guarantees that everything works and the first try. :)
I'm looking at the tutorial for Spatialite-Android, and I'm noticing the following code samples:
String query = "SELECT AsText(Transform(MakePoint(" + TEST_LON + ", " + TEST_LAT + ", 4326), 32632));";
sb.append("Execute query: ").append(query).append("\n");
try {
Stmt stmt = db.prepare(query);
if (stmt.step()) {
String pointStr = stmt.column_string(0);
sb.append("\t").append(TEST_LON + "/" + TEST_LAT + "/EPSG:4326").append(" = ")//
.append(pointStr + "/EPSG:32632").append("...\n");
}
stmt.close();
} catch (Exception e) {
e.printStackTrace();
sb.append(ERROR).append(e.getLocalizedMessage()).append("\n");
}
In particular, I noticed that poor practice is done of simply stringing together a SQL query, instead of a more proper method, such as is used by the Android SQLite library. Is there a way that I can make Spatialite use true prepared statements?
Just to be clear, I'm looking for something like this, using the standard SQLite database in Android:
String query="SELECT * FROM table WHERE _id=?";
Cursor data=db.rawQuery(query,new String[]{id});
There are a few tricks. They all use the exec() call, which has 3 arguments for this version. The statement from the source code is:
public void exec(String sql, jsqlite.Callback cb, String args[])
A jsqlite.Callback is an interface, of which there can be several. But the best way seems to be using a db.get_table(query,args) function. %q is the effective replacement for ? in the Android SQLite representation. Here's the transformation of the given code:
String query = "SELECT AsText(Transform(MakePoint(%q, %q, 4326), 32632));";
TableResult result=db.get_table(query,new String[]{""+TEST_LONG,""+TEST_LAT});
From there, you just have to get the results from TableResult. There isn't a method call to get the results from here, you actually have to grab the publicly declared variable and manually parse through it. Here's an example of how that can be done.
TableResult result=db.get_table(query,new String[]{""+lng,""+lat});
Vector<String[]> rows=result.rows;
for (String[] row:rows)
{
for (String val:row)
{
Log.v(TAG,val);
}
}
If you aren't doing a select, try something like this:
TableResult result=new TableResult();
db.exec("ATTACH DATABASE %q AS newDb",result,new String[]{path});
I assume the same pattern will work for INSERTS and the like
Should I paste the actual public key of my app right into the value of this variable?
Or should I encode it and then whatever the encoded string is, I'd make that string into the value of this variable?
Which should it be?
The public key present in your Android Developer Console (which can be found under 'Edit Profile') is already Base64 encoded. Just copy paste the content of the key in your source file. For example, if you have something like this:
Then in your Security.java:
String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQ......";
As the Google sample code for In-app billing say, you should obfuscate this public key.
Instead of just storing the entire literal string here embedded in the
program, construct the key at runtime from pieces or
use bit manipulation (for example, XOR with some other string) to hide
the actual key. The key itself is not secret information, but we don't
want to make it easy for an attacker to replace the public key with one
of their own and then fake messages from the server.
I use very simple Java code to generate the Java Class that will give me back the public key. The basic idea is to use recursion to recreate the key using inner static class. It's just food for thought.
It's a "good-enough" approach for my niche market. See this stackexchange security question for more information on obfuscation.
public static void main(String[] args) throws Exception {
String className = genClassName();
PrintWriter writer = new PrintWriter("C:\\" + className + ".java", "iso-8859-1");
printClass(className, writer, "XXXXXX-YOUR-PUBLIC-KEY-GOES-HERE-XXXXXXX", true);
writer.close();
}
private static String genClassName() {
return "Class" + UUID.randomUUID().toString().replaceAll("-", "");
}
private static String printClass(String thisClass, PrintWriter writer, String key, boolean root) {
int split = key.length() / 2;
if (split < 10) {
writer.println("public " + (root ? "" : "static") + " class " + thisClass + " {");
writer.println("public static String get() {");
writer.println("return \"" + key + "\";");
writer.println("}");
writer.println("}");
} else {
String first = key.substring(0, split);
String last = key.substring(split, key.length());
writer.println("public " + (root ? "" : "static") + " class " + thisClass + " {");
String class1 = printClass(genClassName(), writer, first, false);
String class2 = printClass(genClassName(), writer, last, false);
writer.println("public static String get() {");
writer.println("return " + class1 + ".get() + " + class2 + ".get();");
writer.println("}");
writer.println("}");
}
return thisClass;
}
You need the public key in the program's source code so that you can check the signature. Yes, there's nonzero, unavoidable risk that a cracker will find it, replace it with a fake, and feed your program fake purchases.
You cannot completely hide the key from prying eyes, but you can obfuscate. You can break up the Base64 string into several string constants in different spots and concatenate them before use. Better give the chunks inconspicuous names (not like MY_PUBLIC_KEY_PART_4). You can also apply an additional layer of soft encryption to it - something like XOR a value. You can add an integrity check - make sure the key has not been spoofed (say, store the hash of a key elsewhere and check). But this is all still security via obscurity - a determined enough hacker will get through.
Also consider ProGuard, the built-in code obfuscation tool.
If you have a server component as part of your app, then you can move most of the elements of your security, including your public key, to your server. On the server, you can generate the nonce and verify the purchase (I've moved mine to a RESTFul WCF service). If your server component is .NET based, then you'll probably have to generate a modulus and an exponent from your public key so that you can use the RNGCryptoServiceProvider class. There's a Google I/O video which gives an overview to In-App Billing amongst others.