Hello.
I am asking for the best means to implement this via CakePHP. Could you please direct me to which technologies of Cake I’d best be using, and a simple outline of the structure of it?
I am specifically concerned with the security access, having authorised logged in users, all SSL traffic, session tokens and a means to prevent cross-script and brute force attacks.
My situation is I [will] have a website which extracts data that’s on a Windows PC. I have already written the web-broker app on the PC, and all the communications with it. The PC communicates with the web-server via long polling, where the PC fulfills any requests and sends the relevant data back to the webpage.
This is the full source of my prototype. The actions it accepts are: -
‘d’ : The Data request from the webpage via AJAX, it returns with the fulfilled data from my PC,
‘p’ : The long Poll request from my PC checking if there are any data requests to be fulfilled, and
‘r’ : The Returned data sent to the server from my PC which can be passed back to the webpage.
<?php
//server.php
$action = filter_input(INPUT_POST, 'action');
if (strlen($action) !== 1 || strpos('pdr', $action) < 0) //Poll server / Data / Return request
die("Error: Unknown action");
if ($action == 'p' || $action == 'r') { //only my computer is allowed to poll or return request
$csrf = filter_input(INPUT_POST, 'csrf');
if ($csrf != 'someUniqueStringGoesHereWhichIsSolelyKnownByMyPC') //To be complemented by session tokens?
die("Error: CSRF Failed");
}
//some pre-validation, cut down on traffic before SQL
if ($action == 'd') { //query from customer
$account = filter_input(INPUT_POST, 'account');
if ($account == "")
die("Enter account");
if (strlen($account) > 8)
die("Invalid account");
$pincode = filter_input(INPUT_POST, 'pincode');
if ($pincode == "")
die("Enter your pin");
if (strlen($pincode) > 4)
die("Invalid pin");
}
require "../settings.php"; //lazy place to easily store settings out of reach from the Internet, parent of the web root.
//Be aware I used ".." - if this server.php is not in the webroot then the settings.php is not out of reach.
if ($db_server == "")
die("Problem getting database settings.");
$mysqli = mysqli_connect($db_server, $db_username, $db_password, $db_database_wb); //vars from the required settings.php
if (!$mysqli)
die("Specific database not found.");
if ($action == 'd') { //query for customer wanting to see his data
$account = mysqli_real_escape_string($mysqli, $account); //clean up some
$pincode = mysqli_real_escape_string($mysqli, $pincode);
$stmt = $mysqli->prepare('INSERT INTO requests (account, pincode, status, html) VALUES (?, ?, "D", "");');
if (!$stmt)
die("Error: " . $mysqli->error);
if (!$stmt->bind_param("ss", $account, $pincode))
die("Error: " . $mysqli->error);
if (!$stmt->execute())
die("Error: " . $mysqli->error);
$request_id = $mysqli->insert_id;
$stmt->close();
$stmt = $mysqli->prepare('SELECT html FROM requests WHERE account=? AND status="Z" AND request_id=?;');
if (!$stmt)
die("Error: " . $mysqli->error);
if (!$stmt->bind_param("si", $account, $request_id)) //double check on account code
die("Error: " . $mysqli->error);
$expire = time() + 20; //seconds
while (time() < $expire) { //If my server crashes here somehow, the Z record will exist forever...
usleep(10000); //10 ms //But it should never be referenced again, so not bad, just orphaned.
if (!$stmt->execute())
die("Error: " . $mysqli->error);
$found = $stmt->get_result();
if (mysqli_num_rows($found) > 0)
break;
}
$stmt->close();
if (mysqli_num_rows($found) > 0)
echo mysqli_fetch_row($found)[0];
else
echo "Request timed out";
$stmt = $mysqli->prepare('DELETE FROM requests WHERE request_id=?;');
if (!$stmt)
die("Error: " . $mysqli->error);
if (!$stmt->bind_param("i", $request_id))
die("Error: " . $mysqli->error);
if (!$stmt->execute())
die("Error: " . $mysqli->error);
$stmt->close();
}
if ($action == 'p') { //long poll for broker
$expire = time() + 30; //seconds
$stmt = $mysqli->prepare('SELECT CAST(request_id AS CHAR), account, pincode, status ' . //CAST needed for "'s in JSON
'FROM requests WHERE status="D";');
if (!$stmt)
die("Error: " . $mysqli->error);
while (time() < $expire) {
if (!$stmt->execute())
die("Error: " . $mysqli->error);
$result = $stmt->get_result();
while ($row = mysqli_fetch_row($result))
$table_data[] = $row;
$result = json_encode($table_data);
if ($result !== "null") {
// file_put_contents($expire . ".txt", $result);
break;
}
usleep(10000); //10 ms
}
echo $result; //return the whole dataset
}
if ($action == 'r') { //PC replied
$request_id = mysqli_real_escape_string($mysqli, filter_input(INPUT_POST, 'request_id'));
$account = mysqli_real_escape_string($mysqli, filter_input(INPUT_POST, 'account'));
$html = filter_input(INPUT_POST, 'html');
$stmt = $mysqli->prepare('UPDATE requests SET status="Z", html=? WHERE account=? AND request_id=?;');
if (!$stmt)
die("Error: " . $mysqli->error);
if (!$stmt->bind_param("ssi", $html, $account, $request_id)) //double check on account code
die("Error: " . $mysqli->error);
if (!$stmt->execute())
die("Error: " . $mysqli->error);
$stmt->close();
}
mysqli_close($mysqli);
At the moment this could be flooded by illegitimate requests which I would like to negate. Also, much of the data can be highly sensitive (like Human Resource info) so I can’t emphasize the security aspects enough! (Which is why I chose to support CakePHP for the solution to this in the first place.)
If you need any other information, like the structure of the table used, or the html + js code, or even the windows app doing the brokering please don’t hesitate to ask.
I am not asking this casually. I have done several of the tutorials several times, and have read the online book end-to-end. So please any hints, guides or even code would be greatly appreciated.
Also note this is just the back-end part - I’ve yet to start the actual webpage itself in CakePHP, but one hurdle at a time
Thanks
Jonathan