6

I am not very experienced in handling databases.

I have an Android Application that is supposed to send queries to and get the subsequent result set from a remote database sever (MySQL).

I have done this by actually sending the query in the form of a JSON to the server. The server script (PHP) then fetches the query and executes it. The result set is then parsed into a JSON again and sent back to the Android app.

My PHP script

function run_query(mysqli $con, $query){ $res=$con->query($query); return $res; } $reply = array(); if(isset($_POST['json'])) { require_once __DIR__.'/config.php'; require_once __DIR__.'/DbController.php'; $json = json_decode($_POST['json'], true); $query = $json['query']; $con = (new DbController())->connect(DBNAME); if(!$con){ $reply['suc']=false; $reply['err_msg']=$con->error; } else{ $res = run_query($con, $query); if(gettype($res)=="boolean"){ $reply['query_reply']=$res; $reply['suc']=true; die(json_encode($reply)); } $i=0; $reply['query_reply']= array(); while($row = $res->fetch_row()){ $reply['query_reply'][$i] = array(); for($j=0;$j<sizeof($row);$j++) $reply['query_reply'][$i][$j]=$row[$j]; $i++; } $reply['suc']=true; } echo json_encode($reply); } 

As you can see, the 'query' key contains the entire query string that is executed by the MySQL server.

My question is- does this way contain any security (or other) loopholes that I am not aware of? Also, is there a better way to do this?

One of my project-mates suggest that I should chop the query into distinct sections (Like- "query_type" : "SELECT", "table_name" : "LOGIN_TABLE", "where_args": "WHERE x = x", and so on) and send it to the server and thereafter reconstruct the query there and execute.

However, I do not get how this would help. Any suggestions would be greatly appreciated. Thank you in advance.

4
  • 1
    "As you can see, the 'query' key contains the entire query string that is executed by the MySQL server." - BIG no no. Anyone who knows your endpoint can send lil' Bobby Tables over. Database queries should be handled entirely server-side with appropriate sanitised API endpoints provided to the mobile application. Commented Aug 29, 2017 at 15:37
  • That's a very good point which I didn't think about! And that's probably the best comic on SQL injections I have seen so far. But then how should I actually send the request to the server if I don't send the query itself? Please help. Commented Aug 29, 2017 at 15:50
  • I'm a bit short on time at the moment so I can't properly address your question right now, but is your mobile app supposed to be some sort of database manager where the user can explicitly create queries? Or just a normal client/backend setup for storing user info? And will this app grant INSERT/DELETE permissions or only allow queries? Commented Aug 29, 2017 at 15:53
  • No it will not be a database manager. There will be local databases in each phone but the user cannot explicitly make queries. The queries will be made in the backend to sync the databases on the phones with a centralized server. Like any login-based application does. Commented Aug 29, 2017 at 15:58

4 Answers 4

7
+50

Your approach has many problems. Anyone can reverse-engineer your protocol and execute any query they want on your SQL server. Thus your data is not only readable by anyone, it is also modifiable by anyone. In other words, you will get hacked.

The usual way this is done is to split the cake into layers. This means defining an API, built of clear and well-specified methods, with input parameter types, return values, and permissions.

This API can be implemented in any way you like, jsonrpc, SOAP, xmlrpc, your choice, even HTTP GET to a php script returning json would work.

The last option is a bit clunky, but also nice, as it allows you the same api from the javascript running inside your website. No need to have two competing APIs.

An example:

API get_user_profile( user_id INT );

INPUT: integer id of the user

RETURNS: the line in table users for this user, dependent on their permissions.

Since the API executes inside an authenticated session (using cookies or whatever) it knows what user makes the request. Thus, it will let a user see their phone number/email, but it will not return these fields to other users, unless they're admin (that's a simple example of permissions, more complex is possible of course).

So, every operation needs its own API. Some are complicated, for example a generic search. Instead of writing your own mini-language and juggling with parameters to specify the search options, you can simplify things by making it act more or less like a website. The client sends whatever the user typed in the search fields to the server (like a HTTP form) and the server decides how to deal with it.

Obviously, if whatever parameters of your API are bluntly inserted into SQL queries, then SQL injection means you also get hacked. So you need to do it right, just like any website, stuff that is exposed to the evil internets constantly gets attacked.

Think of the client as a browser, the API calls as the URLs, forms, xmlhttprequest etc, and the server as PHP or whatever other server side language. That's basically what it is.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks so much for the detailed answer! Can you please send some links where I can learn more about using such APIs and more. As I said... I'm only a student, a beginner who knows almost nothing about how to make these transactions secure. Thank you so much.
There are many good libraries which you can use to call web API such as retrofit and volley. You can use this link for a working example of using volley androidhive.info/2016/05/…
For creating a service which can communicate with your database on your server, you can write that using node.js which is very easy and fast. Take a look into this google.co.in/…
Yeah, listen to Rizwan, you don't want to write the details yourself, use a library which will handle parameter encoding&decoding, finding which function to call, security, etc. Can't quote any at the moment, your problem is likely to be that there are so many of them to choose from!
Thank you so much guys! I have been thinking about node and volley like you said. I guess that would be the best thing to do.
|
2

Rest api are the best method for android to remote server communication. HttpClient,volley and Fast android network library are some libraries which simplify the rest api communication. HttpClient is too slow so usage of HttpClient is not recomended. Fast Android network library is the recommended because it is simple and fast.

Usage of Fast Android Network Library

Tutorial of fast android network library

1 Comment

Please send me your email id if you not clear with above link. I will give you a sample
0

The best way to communicate between client side application and a remote database is by using RESTful API with it's famous HTTP requests GET, PUT, POST and DELETE. When you have a REST API you can have a multiple client side apps using the same database, like Android, IOS or JAVASCRIPT. And it will be secured by an API_KEY so only requests that are authorized to do the queries or modifications will be accepted.
There are many ways to create an REST API since you are a PHP developer I would recommend Slim PHP framework since it's lightweight and easy to use.

Comments

0

suggestions 1 : Add mySql user with certain permissions. or add permissions to sensitive tables like tables that have other user's information ...

suggestions 2 : Create an access key for every devices, refresh it in every request and give new access key as responds, if access key invalid ask the device to login in again. if login is successful, give that device new access key.

  • keep a crone to clear inactive devices in time interval wises
  • keep access key in SharedPreference in android devices

please check YQL they had same working idea

TRY THIS CLASS instead of volley

public class SyncToServer extends AsyncTask<String,String,String> { Context context; String url; JSONObject jsonObjectToSend; JSONArray jsonArrayToSend; OnWebServiceListener onWebServiceListener; ProgressDialog progressDialog; /** * basic initializing, only use this contractor if you don't have anything to send. * @param context it can be null, if you don't want {@link ProgressDialog} pass it as null * @param url the url for data to be passed */ public SyncToServer(Context context,String url){ this.context=context; this.url=url+""; Log.e("SyncToServer123",url+""); if(context!=null) { progressDialog = new ProgressDialog(context); progressDialog.setMessage("loading"); progressDialog.setCancelable(false); } jsonObjectToSend=null; } /** * basic initializing, only use this contractor if you had anything to send. * @param context it can be null, if you don't want {@link ProgressDialog} pass it as null * @param url the url for data to be passed * @param jsonObject the {@link JSONObject} to pass server */ public SyncToServer(Context context,String url,JSONObject jsonObject){ this.context=context; this.url=url; this.jsonObjectToSend=jsonObject; if(context!=null) { progressDialog = new ProgressDialog(context); progressDialog.setMessage("loading"); progressDialog.setCancelable(false); } } /** * basic initializing, only use this contractor if you had anything to send. * @param context it can be null, if you don't want {@link ProgressDialog} pass it as null * @param url the url for data to be passed * @param jsonObject the {@link JSONArray} to pass server */ public SyncToServer(Context context,String url,JSONArray jsonObject){ this.context=context; this.url=url; this.jsonArrayToSend=jsonObject; if(context!=null) { progressDialog = new ProgressDialog(context); progressDialog.setMessage("loading"); progressDialog.setCancelable(false); } } /** * this method will register a callback, and start web service * @param onWebServiceListener this interface used to callback mechanism */ public void start(OnWebServiceListener onWebServiceListener){ Log.i("SyncToServer123","start"); this.onWebServiceListener=onWebServiceListener; this.execute(); } @Override protected void onPreExecute() { super.onPreExecute(); Log.i("SyncToServer123","init onPre"); if(context!=null) { progressDialog.show(); } } @Override protected String doInBackground(String... strings) { HttpClient client = new DefaultHttpClient(); Log.i("SyncToServer123","doIn"); try { InputStream is; HttpPost post = new HttpPost(url); HttpResponse httpResponse; if (jsonObjectToSend != null) { Log.i("SyncToServer123", jsonObjectToSend.toString()); StringEntity se = new StringEntity(jsonObjectToSend.toString()); Log.i("SyncToServer123", jsonObjectToSend.toString()); se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json")); post.setEntity(se); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); post.setHeader("connection", "close"); httpResponse = client.execute(post); }else if(jsonArrayToSend!=null){ Log.i("SyncToServer123", jsonArrayToSend.toString()); StringEntity se = new StringEntity(jsonArrayToSend.toString()); Log.i("SyncToServer123", jsonArrayToSend.toString()); se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json")); post.setEntity(se); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); post.setHeader("connection", "close"); httpResponse = client.execute(post); }else{ HttpGet httpGet=new HttpGet(url); httpGet.setHeader("connection", "close"); httpResponse = client.execute(httpGet); } HttpEntity httpEntity = httpResponse.getEntity(); is = httpEntity.getContent(); try { BufferedReader reader = new BufferedReader(new InputStreamReader( is, "iso-8859-1"), 8); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line + ""); System.out.println(line); Log.i("SyncToServer123","PRE RESPONDS : "+line); } is.close(); String json = sb.toString(); JSONObject jObj; jObj = new JSONObject(json); Log.i("resultJson",json); Log.i("SyncToServer123","RESPONDS : "+json); reader.close(); httpEntity.consumeContent(); if(client != null && client.getConnectionManager() != null) { client.getConnectionManager().shutdown(); } return json; } catch (Exception e) { Log.i("SyncToServer123","Error "+e); Log.e("Buffer Error", "Error converting result " + e.toString()); } //customDbHelper.notifiSyncCompleted(); } catch (Exception e) { Log.i("SyncToServer123","Error "+e); e.printStackTrace(); } return null; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); System.out.print("SyncToServer123: "+s); if(s!=null){ this.onWebServiceListener.onComplete(s); }else{ this.onWebServiceListener.onError(); } if(context!=null) { progressDialog.dismiss(); } } /** * for callbacks the web service status, */ public interface OnWebServiceListener{ /** * when service completed without any exception, including {@link JSONException} * @param result the responds given from server */ void onComplete(String result); /** * if any exception occurred this method will call */ void onError(); } } 

USAGE

SyncToServer syncToServer=new SyncToServer(null,"url",jsonObject); syncToServer.start(new SyncToServer.OnWebServiceListener() { @Override public void onComplete(String result) { } @Override public void onError() { } }); 

Library used

 useLibrary 'org.apache.http.legacy' 

6 Comments

I think using volley or fast would be good, as others noted. Besides, I came to know that Google advices Android Developers to use volley.
volley lib is easy to use, it have too many other options, send NamePairValue, post files to server.
in my case i created a library, for POST and read data from server, and with access key mechanism,
@DebanikDawn please check that class
"keep your project small", one of the best way to performance. what i mean is not every project required all the capabilities that volley provide.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.