<?php
header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json');
$mysql = mysql_connect('corte.no-ip.org', 'hostcorte', 'xxxx');
mysql_select_db('fotosida');
if((isset($_POST['GetPersons'])))
{
if(isset($_POST['ID'])) {
$query = sprintf("SELECT * FROM persons WHERE id='%s'",
mysql_real_escape_string($_POST['ID']));
} else {
$query = "SELECT * FROM persons";
}
$res = mysql_query($query);
while ($row = mysql_fetch_assoc($res)) {
for ($i=0; $i < mysql_num_fields($res); $i++) {
$info = mysql_fetch_field($res, $i);
$type = $info->type;
if ($type == 'real')
$row[$info->name] = doubleval($row[$info->name]);
if ($type == 'int')
$row[$info->name] = intval($row[$info->name]);
}
$rows[] = $row;
}
echo json_encode($rows);
}
mysql_close($mysql);
?>
This works ok for generating a json object based on a database query. Im not very familiar with PHP, so i would like some feedback from you before i proceed with this. Is this a good way of calling the database using ajax? Other alternatives? Frameworks maybe?Are there any security problems when passing database queries like UPDATE, INSERT, SELECT etc using an ajax HTTPPOST? Thanks
To simplify CRUD operations definitely give REST a read.
As mentioned, stop using the @
(AKA "shut-up") operator in favor of more robust validation:
if(isset($_GET['key'])){
$value = $_GET['key'];
}
Or some such equivalent.
Using JavaScript/AJAX, aggregate and send your request data, such as IDs and other parameters, from the form fields into a JSON object. Not the built query. The only time the client should be allowed to manipulate directly executed SQL is if you're creating an web based SQL client. Architect your URLs meaninfully (RESTful URLs) so that your HTTP request can be formed as:
GET users/?id=123
DELETE photos/?id=456
Or alternatively:
GET users/?id=123
GET photos/?method=delete&id=456
Server-side, you're going to receive these requests and based on parameters from the session, the request, etc., you can proceed by firing parametrized queries:
switch($method){
case 'get':
$sql = 'SELECT * FROM `my_table` WHERE `id` = :id';
break;
case 'delete':
$sql = 'DELETE FROM `my_table` WHERE `id` = :id';
break;
default:
// unsupported
}
// interpolate data from $_GET['id'] and fire using your preferred
// database API, I suggest the PDO wrapper.
See PDO
Generate output as necessary, and output. Capture on client-side and display.
Always validate and filter user input. Never send and execute raw SQL queries, or concatenate raw user input into SQL queries.
With regard to your question, here's a possible snippet:
(Note -- I haven't tested it, nor rigorously reviewed it, but it should still serve as a guide -- there is a lot of room for improvement, such as refactoring much of this logic into reusable parts; functions, classes, includes, etc.)
header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json');
$error = array();
// get action parameter, or use default
if(empty($_POST['action']))
{
$action = 'default_action';
}
else
{
$action = $_POST['action'];
}
// try to connect, on failure push to error
try
{
$pdo = new PDO('mysql:dbname=fotosida;host=corte.no-ip.org', 'hostcorte', 'xxxx');
}
catch(Exception $exception)
{
$error[] = 'Error: Could not connect to database.';
}
// if no errors, then check action against supported
if(empty($error))
{
switch($action)
{
// get_persons action
case 'get_persons':
try
{
if(!isset($_POST['id']))
{
$sql = 'SELECT * FROM `persons`';
$stm = $pdo->prepare($sql);
$stm->execute();
}
else
{
$sql = 'SELECT * FROM `persons` WHERE `id` = :id';
$stm = $pdo->prepare($sql);
$stm->execute(array(
'id' => (int) $_POST['id'],
));
}
$rows = array();
foreach($stm->fetchAll() as $row)
{
$rows[] = $row;
}
}
catch(Exception $exception)
{
$error[] = 'Error: ' . $exception->getMessage();
}
break;
// more actions
case 'some_other_action':
// ...
break;
// unsupported action
default:
$error[] = 'Error: Unsupported action';
break;
}
}
// if errors not empty, dump errors
if(!empty($error))
{
exit(json_encode($error));
}
// otherwise, dump data
if(!empty($rows))
{
exit(json_encode($rows));
}
You shouldn't trust your users so much! Always take into account, when working with Javascript, that an user could edit your calls to send what (s)he wants.
Here you are taking the query from the GET parameters and executing it without any kind of protection. How can you trust what $_GET['query']
contains? A way to do this would be to call a php page with some parameters through ajax, validate them using PHP and then execute a query built on the parameters you get, always thinking about what the values of such parameters could be.
You can't do that. Sending database queries from the client is a huge security risk! What if he sends DROP TABLE fotosida
as query?
You should always validate and sanitize data coming from the client before you do anything with it. Identify your use-cases and provide access to them with a clearly defined interface.
Update: To elaborate a bit about the interface you define. Say you're creating a gallery. Let's assume you have several use-cases:
There are different ways to do this, but the simplest way (for a beginner in PHP programming) is proably to have a PHP script for every case.
So you'll have:
imageList.php?gallery=1
that will return a list of all images in the gallery with ID 1deleteImage.php?image=46
will delete the image with ID 46uploadImage.php
parameters will be passed via multipart POST and should be a uploaded file and the ID of the gallery where the image should be added to.All these scripts need to make sure that they are receiving valid parameters. Eg. the ID should be a number, uploaded file needs to be checked for validity etc.
Only expose the needed functionality via your interface. This makes it much more secure and also better understandable for other users.
Like the other answers above, i agree that this is just asking for an injection attack (and probably other types). Some things that you can do to prevent that and enhance security in other ways could be the following:
1 Look for something suspicious with your response handler. Lack of a query variable in the post, for instance, doesn't make sense, so it should just kill the process.
@$_POST["query"] or die('Restricted access');
2 Use preg_match to sanatize specific fields.
if (!preg_match("/^[a-zA-Z0-9]+$/", $_POST[query])){
die('Restricted access');
}
3 Use more fields, even if they are semi-meaningless and hidden, to add more reasons to kill the process through their absence, or lack of a certain text pattern (optional).
4 You shouldn't send a complete query through the POST at all. Just the elements that are necessary as input from the user. This will let you build the query in PHP and have more control of what actually makes it to the final query. Also the user doesn't need to know your table names
5 Use mysql_real_escape_string on the posted data to turn command characters into literal characters before entering data into a db. This way someone would have a last name of DROP TABLE whatever, instead of actually dropping table whatever.
$firstname = mysql_real_escape_string($_POST[fname]);
$lastname = mysql_real_escape_string($_POST[lname]);
$email = mysql_real_escape_string($_POST[email]);
$sql="INSERT INTO someTable (firstname, lastname, email)
VALUES('$firstname','$lastname','$email')";
6 Last, but not least, be creative, and find more reasons to kill your application, while at the same time giving the same die message on every die statement (once debugging is done). This way if someone is hacking you, you don't give them any feedback that they are getting through some of your obstacles.
There's always room for more security, but this should help a little.