我在处理数据库方面不是很有经验。
我有一个Android应用程序,它应该从远程数据库服务器(MySQL)发送查询并从远程数据库服务器(MySQL)获取后续结果集。
我通过实际以JSON的形式将查询发送到服务器来完成此操作。然后,服务器脚本(PHP)将获取查询并执行它。然后将结果集再次解析为JSON并发送回Android应用程序。
我的PHP脚本
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);
}
如您所见,'query'
键包含MySQL服务器执行的整个查询字符串。
我的问题是 - 这种方式是否包含我不知道的任何安全(或其他)漏洞?另外,有更好的方法吗?
我的一个项目伙伴建议我将查询分成不同的部分(Like-&#34; query_type&#34;:&#34; SELECT&#34;,&#34; table_name&#34;:& #34; LOGIN_TABLE&#34;,&#34; where_args&#34;:&#34; WHERE x = x&#34;等等)并将其发送到服务器,然后在那里重建查询并执行。
但是,我不明白这会有什么帮助。任何建议将不胜感激。提前谢谢。
答案 0 :(得分:6)
你的方法有很多问题。任何人都可以对您的协议进行反向工程并在SQL服务器上执行他们想要的任何查询。因此,您的数据不仅可供任何人阅读,也可由任何人修改。换句话说,你会被黑客入侵。
通常的做法是将蛋糕分成几层。这意味着使用输入参数类型,返回值和权限定义一个由明确且指定良好的方法构建的API。
这个API可以以你喜欢的任何方式实现,jsonrpc,SOAP,xmlrpc,你的选择,甚至HTTP GET到一个返回json的php脚本都可以工作。
最后一个选项有点笨重,但也很好,因为它允许你从你的网站内运行的javascript相同的api。无需拥有两个竞争API。
一个例子:
API get_user_profile(user_id INT);
INPUT:用户的整数id
RETURNS:此用户的表用户中的行,具体取决于他们的权限。
由于API在经过身份验证的会话中执行(使用cookie或其他),因此它知道用户发出请求的内容。因此,它会让用户看到他们的电话号码/电子邮件,但不会将这些字段返回给其他用户,除非他们是管理员(这是一个简单的权限示例,当然可能更复杂)
因此,每个操作都需要自己的API。有些很复杂,例如通用搜索。您可以通过使其行为或多或少像网站一样简化,而不是编写自己的迷你语言并使用参数来指定搜索选项。客户端将用户在搜索字段中键入的内容发送到服务器(如HTTP表单),服务器决定如何处理它。
显然,如果您的API的任何参数直接插入到SQL查询中,那么SQL注入意味着您也会被黑客入侵。因此,你需要做到正确,就像任何网站一样,暴露在邪恶互联网中的东西不断受到攻击。
将客户端视为浏览器,API将URL,表单,xmlhttprequest等称为服务器,将服务器称为PHP或其他任何服务器端语言。这基本上就是它。
答案 1 :(得分:1)
Rest api是android到远程服务器通信的最佳方法。 HttpClient,volley和Fast android网络库是一些简化其余api通信的库。 HttpClient太慢,因此不建议使用HttpClient。建议使用快速的Android网络库,因为它简单快速。
快速Android网络库的使用
答案 2 :(得分:0)
在客户端应用程序和远程数据库之间进行通信的最佳方式是使用RESTful API
及其着名的HTTP
请求GET, PUT, POST and DELETE
。当您拥有REST API
时,您可以使用同一个数据库拥有多个客户端应用,例如Android
,IOS
或JAVASCRIPT
。它将由API_KEY
保护,因此只有被授权进行查询或修改的请求才会被接受。
创建REST API
的方法有很多种,因为您是PHP开发人员我会推荐Slim PHP framework,因为它的重量轻且易于使用。
答案 3 :(得分:0)
建议1:添加具有特定权限的mySql用户。或者向敏感表添加权限,例如具有其他用户信息的表...
建议2:为每个设备创建一个访问密钥,在每个请求中刷新它,并在响应时提供新的访问密钥,如果访问密钥无效则要求设备再次登录。如果登录成功,请为该设备提供新的访问密钥。
请检查YQL他们是否有同样的工作想法
尝试此类而不是volley
b= function(a,n){
lst=sapply(1:(length(a)-n),function(t,a,n){a[t:(n+t-1)]},a,n,simplify = F)
Reduce(function(x,y){rbind(x,y)},lst)
}
<强> USAGE 强>
var app = angular.module('EntityApp', ['ngRoute', 'slickCarousel']);
app.controller('EntityAppCntroller', function($scope, $http, $window) {
if (localStorage.getItem('IsNewPinSet') != 1) {
var getLocation = function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
};
getLocation();
function showPosition(position) {
var latitude;
var longitude;
latitude = position.coords.latitude;
longitude = position.coords.longitude;
localStorage.setItem('latitudeHome', latitude)
localStorage.setItem('longitudeHome', longitude)
var geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(latitude, longitude);
geocoder.geocode({
'latLng': latlng
}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0]) {
for (var i = 0; i < results[0].address_components.length; i++) {
var types = results[0].address_components[i].types;
for (var typeIdx = 0; typeIdx < types.length; typeIdx++) {
if (types[typeIdx] == 'postal_code') {
var pin_all = results[0].address_components[i].short_name;
localStorage.setItem('pincodehome', pin_all)
$scope.pin_all = pin_all;
}
}
}
} else {
console.log("No results found");
}
}
});
};
}
// $window.localStorage.getItem('pincodehome') is null
// $window.localStorage.getItem('latitudeHome') is null
// $window.localStorage.getItem('longitudeHome') is null
//Common response in home page
$http.get('someurl?pincode=' + localStorage.getItem('pincodehome') + '&lat=' + localStorage.getItem('latitudeHome') + '&lang=' + localStorage.getItem('longitudeHome'), {}).success(function(response) {
$scope.Promotion = response.Promotion.Response;
});
});
使用了库
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();
}
}