web services and phonegap : best practices - javascript

Hi I am using phonegap for crossed plateform development (I use angularJS as JS framework).
I want to use a web service to access to a list of positions from my database (mysql) on my website.
The problem is that the solution I found is not secure at all:
Javascript
var xhr;
if (window.XMLHttpRequest)
xhr = new XMLHttpRequest();
else
xhr = ActiveXObject("Microsoft.XMLHTTP");
xhr.open("GET", "http://localhost:8888/MAMP_Site/0/test.php", true);
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
console.log("Ready State4: Json Textual Data retrieved");
handleData(xhr.responseText); // Json Textual Data
}
};
function handleData(data)
{
var jsonData;
console.log("ReceivedData from WebService:"+data);
jsonData = eval('(' + data + ')');
$scope.lastUpdate = jsonData[0];
$scope.jsonData = jsonData[1];
$scope.$apply();
}
PHP (used as "web service")
<?php
header('Access-Control-Allow-Origin: *');
header("Content-Type: text/plain");
class UserInfo {
public $id = "";
public $name = "";
public $username = "";
public $timestamp = "";
public function __construct($_id, $_name, $_username, $_timestamp) {
$this->id = $_id;
$this->name = $_name;
$this->username = $_username;
$this->timestamp = $_timestamp;
}
}
$db = mysql_connect('localhost:8889', 'root', 'root');
mysql_select_db('myDbName',$db);
$sql = 'SELECT id,name,username,timestamp FROM positions_test';
$req = mysql_query($sql) or die('Erreur SQL !<br>'.$sql.'<br>'.mysql_error());
$dataArray = array();
while($data = mysql_fetch_assoc($req)) {
$dataArray[]= new UserInfo($data['id'],$data['name'],$data['username'],$data['timestamp']);
}
//Last Modified Time
$sql = "SELECT UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = 'myDbName'AND TABLE_NAME = 'positions_test'";
$req = mysql_query($sql) or die('Erreur SQL !<br>'.$sql.'<br>'.mysql_error());
$data = mysql_fetch_assoc($req)["UPDATE_TIME"];
$jsonDataArray = array($data, $dataArray);
echo json_encode($jsonDataArray);
mysql_close();
?>
Basically the PHP return a JSON (as text), and I get it (as text) in my JS. Then I evaluate it as a JSON.
The question
Security concern
As the application is made with cordova, all JS and Html source code can be viewed and so the URL of my php "web service". It means that anybody who have the adress can access to the Json File. Even if this data is public (in my case) I want it to be only accessible from my app (this way I can for instance avoid a bot to store all of this data and spam).
Token or user-agent
As there is no authentification for users is there any way for my webservice to know where the request come from?
I thought using a token to ensure that the request come from my app but once again as the source code can be viewed, anybody could see the token or the code to generate it.
Maybe using user-agent to know if it is accessed from a mobile device?
Other port than 80
Maybe it would be judicious to choose another port than 80 to connect to my web service, but how can I select my connexion port?
Best practice
The main point would actually be, what are the best practice for web services on phonegap (cordova) ?
Should I use SSL, Https?
Should I use a real web service instead of a simple php page and XMLHTTPRequest? If yes, which one?
And of course how building properly and securely my web service ?
I know this is a long post, but I searched the web a for while and I found a lot of interesting stuff but nothing really concret on the best practices to build your web services for a phonegap application (with no user authentification)

You could try to obfuscate it, or a a lot of other things, but in the end you have to receive it in the client side and therefore there is nothing you can do to fully prevent him from reading your data, seeing your client side code or spamming your service.
The best you can do to make sure that the service is safe is: make sure the connection to the db does not allow writes, all the software involved is updated regularly and that the queries sent to your service have the syntax and content that you are expecting.

Related

Workaround to XHR request and download prohibition

I have a weird situation:
I get data from a Postgres database, and from that data, I create a table in a website, using Grid.js. Each line has a "Download" button, that takes 2 arguments from that table entry and send them to a function. Originally, that function would make a XHR request to a php file, that gets files from another DB, creates a ZIP file, and should send it to the user, with readfile().
I now discovered that this is not possible. XHR does not allow downloads for safety reasons.
I could do something using window.location to call the PHP file, and get the download, but I am dealing with hundreds of files, so I cannot write hundreds of PHP files to get the data individually. I could, but it would be very hard to maintain and manage all those files, and it feels unprofessional.
Right now, I can:
Send the data from JS to PHP, using POST;
Using those two variables, fetch the data from another Postgres server;
Use those files and create a ZIP file (The ZIP files cannot be stored permanently in the server, because of storage restrictions. A cronjob in the server will clean the files eventually)
I need to:
Send the ZIP to to the user;
Maintain the simplest code possible, in a way that I can feed 2 variables and it just works, without needing a PHP file for each table line (if that makes sense)
The current code is:
Javascript
const getData = (schema, table) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', 'php/get_data.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
let packg = {
schema: schema,
table: table
};
const packgJSON = JSON.stringify(packg);
// Vanilla JS XHR requires this formatting to send the data
const data = new URLSearchParams({'content': packgJSON});
xhr.send(data);
};
PHP
<?php
// File with connection info
$config = include('config.php');
// Connection info
$host = $config['host'];
$port = $config['port'];
$database = $config['database'];
$username = $config['username'];
$password = $config['password'];
// POST reception
$packg = json_decode($_POST['content']);
$schema = $packg->schema;
$table = $packg->table;
// File info and paths
$filename = $table;
$rootDir = "tempData/";
$fileDir = $filename . "/";
$filePath = $rootDir . $fileDir;
$completeFilePath = $filePath . $filename;
$shpfile = $filename . ".shp";
$zipped = $filename . ".zip";
$zipFile = $completeFilePath . ".zip";
// Function to send the file (PROBLEM - THIS DOES NOT WORK WITH XHR)
function sendZip($zipFile) {
$zipName = basename($zipFile);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=$zipName");
header("Content-Length: " . filesize($zipFile));
ob_flush();
flush();
readfile($zipFile);
};
// Send the zip if it's already available (NOT PROBLEMATIC)
if (file_exists($zipFile)) {
sendZip($zipFile);
};
// Get shapefile, zip it and send it, if not available (NOT PROBLEMATIC)
if (!file_exists($zipFile)) {
// Get files
exec("mkdir -p -m 777 $filePath");
exec("pgsql2shp -h $host -p $port -u $username -P $password -g geom -k -f $completeFilePath $database $schema.$table");
// ZIP the files
$zip = new ZipArchive;
if ($zip -> open($zipFile, ZipArchive::CREATE) === TRUE) {
$handlerDir = opendir($filePath);
// Iterates all files inside folder
while ($handlerFile = readdir($handlerDir)) {
// If the files are indeed files, and not directories
if (is_file($filePath . $handlerFile) && $handlerFile != "." && $handlerFile != "..") {
// Zip them
$zip -> addFile($filePath . $handlerFile, $fileDir . $handlerFile);
};
};
// Close the file
$zip -> close();
};
sendZip($zipFile);
};
?>
As pointed out by #epascarello here, a simple GET request solves this.
Even though I was afraid of not using POST because of an SQL injection attack, the variables pass through a pgsql2shp program, and that only accepts a valid schema and table names, so no need to worry about that as much.
I am currently using this KISS code, and it works:
const getData = (schema, table) => {
window.location='php/get_data.php?schema=' + schema + '&table=' + table;
};
In PHP, it's only needed a small change from the POST reception to a GET reception. The variables are already separated, no need to decode anything:
$schema = $_GET['schema'];
$table = $_GET['table'];
This goes to show that sometimes, we look so deep into a problem that the solution is right in front of us.

Where do I have to define connectors in Alfresco? Can I use them from a ".bpmn" file?

Good afternoon! I am trying to communicate from Alfresco to a RESTful ws from a workflow. Somebody told me that it will be a good idea to use connector to acomplish that. I am creating a wf in ACS as a .bpmn file, so 3 questions:
In what file do I have to define the connector?, I want to do the same as this js script:
var url = "https://google.com";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.status);
console.log(xhr.responseText);
}};
xhr.send();
Can I use the connector directly from the .bpmn file? Could you give me an example on how to use it?
Could you give me an example to make a GET and a POST call?
Thanks in advance!
Connectors are part of the web script framework. There is a web script framework in the Alfresco Share tier and there is a web script framework in the repository tier.
In web scripts, you use the "remote" object to make connections to remote RESTful end points. For example, the code to fetch some project information from an API via its project ID might look like this:
var connector = remote.connect("someapp");
var resp = connector.get("/someapp/projects/" + projectId);
if (resp.status == 200) {
obj = JSON.parse(resp);
model.result = obj;
model.startDateFormatted = getFormattedDate(model.result.StartDate);
model.endDateFormatted = getFormattedDate(model.result.EndDate);
}
So where is that "someapp" connector defined? You can put it in an Alfresco config file such as the share custom config. In an Alfresco SDK based project that file lives in the share module under ./src/main/resources/META-INF/share-config-custom.xml:
<config evaluator="string-compare" condition="Remote">
<remote>
<endpoint>
<id>someapp</id>
<name>Some Remote App</name>
<connector-id>http</connector-id>
<endpoint-url>https://someapp.example.org</endpoint-url>
</endpoint>
</remote>
</config>
The challenge for you is that you are running in an Activiti workflow, not a web script. And, aside from that, the remote object is disabled on the repo tier, which is also where you are running. It can be re-enabled, but I don't have those steps handy and it doesn't help you anyway.
So, rather than use any of the above, my advice is to use a Java delegate in your workflow. From the Java delegate you can do anything you want, including leveraging something like the Apache HTTP Client to make a connection to the API you need to call.
You might have a delegate class like:
public class RemoteApiInvocationDelegate implements JavaDelegate {
private Logger logger = LoggerFactory.getLogger(RemoteApiInvocationDelegate.class);
public void execute(DelegateExecution execution) throws Exception {
HttpClient httpClient = Utils.noCertClient();
HttpGet httpGet = new HttpGet("http://someapp.example.org/someapp/projects/12345");
HttpResponse response = httpClient.execute(httpGet);
int status = response.getStatusLine().getStatusCode();
logger.info("Response Status Code: " + status);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line;
StringBuilder sb = new StringBuilder();
while ((line = rd.readLine()) != null) {
sb.append(line);
}
logger.info(sb.toString());
}
}
What you do with the response, like parse it, extract some data, and set it on a process variable, for example, is up to you.
Jeff answer is the most complete. However these are some interesting videos about Java Delegates:
https://www.youtube.com/watch?v=phju1Lru7kI&list=PLOW8iKnFZkm_Eq8MPilFin-lwbCg5J15U&index=1&t=3s
https://www.youtube.com/watch?v=eOL_OKMylfw&list=PLOW8iKnFZkm_Eq8MPilFin-lwbCg5J15U&index=2&t=221s
And this post too:
https://www.armedia.com.mk/spring-managed-alfresco-custom-activiti-java-delegates/

How do I connect Azure SQL to my HTML5 web app

Today I set up a new Azure SQL database and HTML5 Webapp to replace an existing Google Apps Script which wasn't working on iPhones. I have the database connected to the webapp with the connection string, and I want to know how to simply post the details from the html form into the SQL database.
Previously I have been using javascript in GAS with a firebase backend, so I am missing a bit here. Although I can see the connection string in my webapp, I don't know how to then call or post the html form information.
I looks at the functions app method but can't really find the code to post html data, plus how I trigger that from clicking a button on the client side.
I found this code which say it will work, but not to use it because it leaves your database open to hackers.
<script>
var objConnection = new ActiveXObject("adodb.connection");
var strConn = "driver={sql server};server=QITBLRQIPL030;database=adventureworks;uid=sa;password=12345";
objConnection.Open(strConn);
var rs = new ActiveXObject("ADODB.Recordset");
var strQuery = "SELECT * FROM Person.Address";
rs.Open(strQuery, objConnection);
rs.MoveFirst();
while (!rs.EOF) {
document.write(rs.fields(0) + " ");
document.write(rs.fields(1) + " ");
document.write(rs.fields(2) + " ");
document.write(rs.fields(3) + " ");
document.write(rs.fields(4) + "<br/>");
rs.movenext();
}
Can anyone please let me know what I am missing to get the SQL database and html5 web app talking?
Thanks
First of all you should not connect to any database from an HTML app which runs on the client side the user will be browsing the site which have a huge security impact. Connecting to a database (it could be sql,mysql or any) happens on the server side, which is where you host your application.
In order to connect to the database you will have to use server side technologies such as node,php or asp.net etc.

capture file into javascript variable instead of downloading it

I am writing a app that visits a web site that can give me a link to a file, like this: http://www.thrustcurve.org/download.jsp?id=2199
If I visit this link, a small text file is downloaded. What I would like to do instead is to capture this text into a javascript variable so I can search around in it and extract the data I need.
Is this even possible?
Further details: although I am old and have lots of programming experience, I am a total noob in the javascript/web/server/modern space (think FORTRAN 77).
I now teach high school physics and am trying to build a web-based rocket simulator for my students to use on their chromebooks. The creator of thrustcurve.org has generously made data about rocket motors available on the web, but I need some bits that can only be found inside these little text files. Maybe it would be possible to work with the downloaded files on the chrome books, but I really have no idea how to begin there. If you are patient enough to have read this far, you can see the kind of javascript I have been able to accomplish at noragulfa.com
You can use XMLHttpRequest to perform HTTP requests, but due to security restrictions the browser blocks requests to “external domains” (thus, you can download files only from your domain). For more info, read about Cross-Origin Resource Sharing (CORS).
To solve your task, you have several options:
1) Download required files from thrustcurve.org and store them on your server. This is the best option since you will not be dependent on an external server (besides, hotlinking may upset the thrustcurve.org owner). In this case XMLHttpRequest will be able to access files using relative URLs:
var url = '/thrustcurve-downloads/Estes_A8.eng';
2) Contact the thrustcurve.org owner and ask him to enable Access-Control-Allow-Origin from anywhere. In this case XMLHttpRequest will be able to access files using full URLs:
var url = 'http://www.thrustcurve.org/download.jsp?id=2199';
3) Create a proxy that passes HTTP requests to thrustcurve.org. For example, since you are using nginx, you can simple add the following to your configuration file:
location /thrustcurve {
proxy_pass http://www.thrustcurve.org/;
}
In this case XMLHttpRequest will be able to access files using relative URLs:
var url = '/thrustcurve/download.jsp?id=2199';
4) Use third-party proxies (not a very reliable solution, but great for tests). As an example, I will use this option.
var url = 'http://cors-anywhere.herokuapp.com/http://www.thrustcurve.org/download.jsp?id=2199';
var xhr = new XMLHttpRequest();
xhr.onload = function () {
console.log(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'text';
xhr.send();
UPD: A full example how to download files using a XMLHttpRequest and PHP.
1) Create the file thrustcurve.php on your root server with the following contents:
<?php
// Change this to FALSE if don't want to store files locally
$store_files_locally = true;
$id = (int) filter_input(INPUT_GET, 'id');
if ($id > 0) {
if ($store_files_locally) {
// Specify the directory where you want to store engine files
// It will create the directory if it doesn't exist
$dir = __DIR__ . '/thrustcurve-downloads';
if (!is_dir($dir) && !mkdir($dir, true, 0777)) {
http_response_code(500);
die('Cannot create the downloads directory');
}
// If file exists, load the engine from the local file
$file = "{$dir}/{$id}.eng";
if (is_file($file)) {
$engine = file_get_contents($file);
die($engine);
}
}
// Download the engine file from the remote server
$url = "http://www.thrustcurve.org/download.jsp?id={$id}";
$engine = trim(#file_get_contents($url));
// The downloaded file is considered valid engine only if it starts with semicolon
if (strpos($engine, ';') === 0) {
if ($store_files_locally) {
file_put_contents($file, $engine);
}
die($engine);
}
}
http_response_code(404);
echo "File #{$id} not found";
2) To download files using JavaScript, use the following:
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.response);
} else {
console.error(xhr.response);
}
};
xhr.open('GET', '/thrustcurve.php?id=2198');
xhr.responseType = 'text';
xhr.send();

XHR POST to Philips Hue Bridge

I'm working on a bridge-like solution to communicate from an HbbTV-application with some Philips Hue lights (correctly with the gateway-hardware).
As the process is moving forward and the system was working, I'm now at the point that I use a plugin for Firefox that simulates a TV with HbbTV. To do so, I start an apache via XAMPP, on this i have my files which are loaded into Firefox.
Since I did that, I can't send any POST-requests to he Philips gateway, what is correct due same origin policy. I have no access to settings on Philips Hue and so my workarround has to be from clientside only.
My actual try looks like this:
var stringState = "http://" + this.Ip + "/api/" + this.UserId + "/lights/" + this.LightId;
var httpxml = new XMLHttpRequest();
var valueRequest;
console.log("in GetState:" + this.LightId);
httpxml.onreadystatechange = function() {
if (httpxml.readyState == XMLHttpRequest.DONE) {
valueRequest = JSON.parse(httpxml.responseText);
console.log(valueRequest);
console.log(valueRequest.state.on);
that.switchState(valueRequest.state.on);
}
}
httpxml.open('GET',stringState,true);
httpxml.withCredentials = true;
httpxml.setRequestHeader("Content-Type", "application/json");
httpxml.send();
I'm pretty new to developing in JavaScript and Web. I hope someone could lead me on the right road, with some advice and maybe a clear example.
Best regards
Adrian
One of the P-s in XAMPP is for PHP. So a workaround you can do is hosting a PHP page right next to your HTML one (and there will not be any issues with CORS), and let it do the job.
Something like
<?php
$ip=$_REQUEST['ip'];
$user=$_REQUEST['user'];
$light=$_REQUEST['light'];
$ch=curl_init("http://".$ip."/api/".$user."/lights/".$light);
curl_exec($ch);
curl_close($ch);
This is far from anything safe and nice, but may help to get started. Some trivial clues: variables start with $, and . is the operator for string concatenation. $_REQUEST is an array which gets the url parameters, what you should supply in your modified request (where xy.php is the filename of the PHP page):
var stringState = "xy.php?ip=" + this.Ip + "&user=" + this.UserId + "&light=" + this.LightId;
curl is a utility for issuing web requests (you can find it in your XAMPP folders, xampp\apache\curl.exe), and it has bindings for PHP. https://curl.haxx.se/. By default it returns whatever your contoller provides, so the JSON should pass-through. Content-type may or may not be an issue, if it does not work, you can try putting a header("Content-Type: application/json"); right before the curl_exec line.

Categories

Resources