I have php application which gets information from a SAML POST and creates a record in the MySQL database, if the record is already present it just updates it
Here is the code
//getMemberRecord returns true for successful insertion.
$row = $this->getMemberRecord($data);
if ($row) {
//if the row already exists
$this->updateMemberRecord($data)
} else {
// creates a new record
$this->setMemberRecord($data);
}
This code is causing double inserts in the database, we don't have a unique key for the table due to some poor design constraints, but I see two HTTP posts in the access logs happening at the same time.
The create date column is same or differs by a second for the duplicate record.
This issue is happening for only select few, it works for most of them.
The table is innoDB table and we can not use sessions on our architecture.
Any ideas of why this would happen
You said:
I see two HTTP posts in the access logs
You should try avoiding this and have just one http POST invocation
May be it is a problem related to concurrency and mutual exclusion. The provided code must be executed in a mutually exclusion zone, so you must use some semaphore / mutex to prevent simultaneous execution.
If you have two HTTP POST happening your problem is not on the PHP/MYSQL side.
One thing is allowing a second 'transparent' HTTP POST in the HTTP protocol. It's the empty url. If you have an empty GET url in the page most browsers will replay the request which rendered the page. Some recent browser are not doing it, but most of them are still doing it (and it's the official way of HTTP). An empty GET url on a page is for example <img src=""> or < script url=""> but also an url() in a css file.
The fact you have one second between the two posts make me think it's what's happening for you. The POST response page is quite certainly containing an empty Get that the browser fill by replaying the POST... I hate this behaviour.
I found that the double inserts were happening becuase double submits and our application doesnot handle double submits efficiently, I read up on some articles on this, here are some of the solutions
it always best to handle double posts at the server side
best solution is to set a UNIQUE KEY on the table or do a INSERT ON DUPLICATE KEY UPDATE
if you have sessions then use the unique token , one of the technique in this article
http://www.freeopenbook.com/php-hacks/phphks-CHP-6-SECT-6.html
or use can use the Post/Redirect/Get technique which will handle most double submit problems
http://en.wikipedia.org/wiki/Post/Redirect/Get
note: the Double submit problem only happens on a POST request, GET request is immune
public function setMemberRecord($data, $brand_id, $organization_id, $context = null)
{
global $gRegDbManager;
$sql = "insert into member ......"
$gRegDbManager->DbQuery($sql);
// Popuplate the iid from the insert
$params['iid'] = $gRegDbManager->DbLastInsertId();
$data = some operations
return (int)$data;
}
public function getMemberRecord($field, $id, $brand_id, $organization_id, $organization_level_account = null)
{
global $gRegDbManager;
$field = mysql_escape_string($field);
$id = mysql_escape_string($id);
$sql = "SELECT * FROM " . DB_REGISTRATION_DATABASE . ".member WHERE $field = '$id' ";
if($organization_level_account) {
$sql .= "AND organization_fk = " . $organization_id;
} else {
$sql .= "AND brand_fk = " . $brand_id;
}
$sql .= " LIMIT 1";
$results = $gRegDbManager->DbGetAll($sql);
if(count($results) > 0) {
return $results[0];
}
return;
}
/* * ******************************************************************************************************
* Updates member record in the member table
* *******************************************************************************************************
*/
public function updateMemberRecord($id, $changes)
{
global $gRegDbManager;
$id = mysql_escape_string($id);
if(!empty($changes)) {
$sql = "UPDATE " . DB_REGISTRATION_DATABASE . ".member SET ";
foreach($changes as $field => $value) {
$sql .= mysql_escape_string($field) . " = '" . mysql_escape_string($value) . "', ";
}
$sql = rtrim($sql, ", ");
$sql .= " WHERE iid = '$id'";
$gRegDbManager->DbQuery($sql);
} else {
return false;
}
}
Related
This question already has answers here:
Can I mix MySQL APIs in PHP?
(4 answers)
Closed 6 years ago.
I am attempting to implement a click count system. I am using the following code in this link Click here to see code, but changing it to modern standards. Initially I received errors for the msqli_real_escape_ string, but I believed I resolved it(no errors). Now, I am not receiving any errors at all, but the query is not sending into my database. I am using ini_set('display_errors', 1);
error_reporting(E_ALL); for error checking. Also I have my $con and session in and ini file that I call, so the session and connection are not issues.
Does anyone see what I am doing wrong or is there a good way I can check to see what isn't working?
//create current page constant
$curPage = mysqli_real_escape_string($con,htmlspecialchars($_SERVER['PHP_SELF']));
//set number of clicks variable to 0
$clicks = 0;
//do not recount if page currently loaded
if($_SESSION['page'] != $curPage) {
//set current page as session variable
$_SESSION['page'] = $curPage;
$click_sql = "
SELECT *
FROM click_count
WHERE page_url = ?
";
if (!$click_stmt = $con->prepare($click_sql)) {
$click_stmt->bind_param("s", $curPage);
$click_stmt->execute();
$num_rows = $click_stmt->fetchColumn();
if (!$click_stmt->errno) {
// Handle error here
}
$stmt->bind_result($click_id, $page_url, $page_count);
} elseif ($num_rows == 0) {
//try to create new record and set count for new page to 1
//output error message if problem encountered
$click_insert_stmt = "
INSERT INTO click_count
(page_url, page_count)
VALUES(?, ?)";
if(!$click_stmt = $con->prepare($click_insert_stmt)) {
$click_insert_stmt->execute(array('$curPage',1));
echo "Could not create new click counter.";
}
else {
$clicks= 1;
}
} else {
//get number of clicks for page and add 1 fetch(PDO::FETCH_BOTH)
while($click_row = $click_insert_stmt->fetch(PDO::FETCH_BOTH)) {
$clicks = $row['page_count'] + 1;
//update click count in database;
//report error if not updated
$click_update_stmt = "
UPDATE click_count
SET page_count = ?
WHERE page_url = ?
";
if(!$click_stmt = $con->prepare("$click_update_stmt")) {
$click_update_stmt->execute(array('$clicks', '$curPage'));
echo "Could not save new click count for this page.";
}
}
}
}
Edit: New Updated Code
// ********Page count************
//create current page constant
$curPage = mysqli_real_escape_string($con,($_SERVER['PHP_SELF']));
//set number of clicks variable to 0
$clicks = 0;
//do not recount if page currently loaded
if($_SESSION['page'] != $curPage) {
//set current page as session variable
$_SESSION['page'] = $curPage;
$click_sql = "
SELECT *
FROM click_count
WHERE page_url = ?
";
if (!$click_stmt = $con->prepare($click_sql)) {
$click_stmt->bind_param("s", $_SERVER['PHP_SELF']);
$click_stmt->execute();
$num_rows = $click_stmt->fetchColumn();
if (!$click_stmt->errno) {
// Handle error here
}
$stmt->bind_result($click_id, $page_url, $page_count);
} elseif ($num_rows == 0) {
//try to create new record and set count for new page to 1
//output error message if problem encountered
$click_insert_stmt = "
INSERT INTO click_count
(page_url, page_count)
VALUES(?, ?)";
if(!$click_stmt = $con->prepare($click_insert_stmt)) {
$click_insert_stmt->execute(array($curPage,1));
echo "Could not create new click counter.";
}
else {
$clicks= 1;
}
} else {
//get number of clicks for page and add 1 fetch(PDO::FETCH_BOTH)
while($click_row = $click_insert_stmt->fetch(PDO::FETCH_BOTH)) {
$clicks = $row['page_count'] + 1;
//update click count in database;
//report error if not updated
$click_update_stmt = "
UPDATE click_count
SET page_count=page_count+1
WHERE page_url = ?
";
if(!$click_stmt = $con->prepare("$click_update_stmt")) {
$click_update_stmt->execute(array($curPage));
echo "Could not save new click count for this page.";
}
}
}
}
It looks like you're doing a lot of stuff like this:
$click_update_stmt->execute(array('$clicks', '$curPage'));
I'm not sure where you picked up this habit of quoting variables as strings, but you need to drop it. '$x' and $x are two hugely different things. In the first case it's literally '$x' and in the second case it's whatever the $x variable happens to represent.
Fix it like this:
$click_update_stmt->execute(array($clicks, $curPage));
Also since you're using prepared statements, which by the way is great, you do not need to and should not manually escape your values. Applying them to placeholders with bind_param is the safe way of doing it. Doing any other escaping mangles the data.
Just bind directly to the source:
$click_stmt->bind_param("s", $_SERVER['PHP_SELF']);
Don't arbitrarily run things like htmlspecialchars on input out of paranoia or because you're doing cargo-cult programming and you saw it done in a YouTube tutorial somewhere. That function is intended to be used to display values only, not store them. Data in your database should be as raw as possible.
There's a lot of problems with this code, and one of them that has me confused is why there's so much code. Remember SELECT * and then binding results to arbitrary variables is trouble, your schema might change and then your code is out of sync. Whenever possible fetch rows as an associative array if doing this, then all you have to worry about is renamed ore removed columns.
The biggest problem is this is subject to race conditions because it doesn't use an atomic increment. When writing counters, always do your updates as operations that are a single statement:
UPDATE click_count SET page_count=page_count+1 WHERE page_url=?
Your approach of reading the count, incrementing it, and then writing it back into the database means that you're inviting problems if another operation runs concurrently, something very likely on click-counter code.
I did make a post previously but was not able to properly explain my issue nor was I able to get it resolved. This is what I have.
$shoutlines = file($shout_file);
$aTemp = array();
foreach($matches['user'] as $user) {
$aTemp[] = "'" . $user . "'";
}
$user = implode(",", $aTemp);
$rara = "SELECT * FROM accounts WHERE username IN ( $user )"; // Tried this statment both as a query and prepared statement
$getlevel = $db->query("SELECT * FROM accounts WHERE username IN '( ".$user." )'"); // Tried this both as a query and prepared statement
//$getlevel->bind_param('s', $user);
//$getlevel->execute();
//$level = $getlevel->get_result();
//$getlevel->store_result();
while($getdb = $getlevel->fetch_assoc()){
//output the html
for($i = 0; $i < (1000); $i++)
{
if(isset($shoutlines[$i]))
{
$shoutline = preg_replace('/<\/div>\n/', ' ', $shoutlines[$i], 1);
echo showSmileys($shoutline) . "<div class='delete'><a href='javascript: delete_shoutline({$i});' title='Delele'>delete</a></div></div>";
}
}
}
I have a for loop within the while loop that will not run within it, if I move the for loop outside of the while it works fine, but I need it in the while loop to make checks of the users for post titles, abilities etc., that are saved in my database. I have shown what I have tried so far when to comes to identifying the problem, I have tried dieing out errors if the query, binds, or executes weren't showing true, but got now hits. The code for this is pulled out so there isn't too much clutter for your reading abilities, any help with this would be greatly appreciated.
When "exploding" the username, you need ot wrap each username in quotes, not the whole thing. Also make the names safe for data entry.
$aTemp = array();
foreach($matches['user'] as $user) {
$aTemp[] = '"' . mysql_real_escape_string($user) . '"';
}
$user = implode(",", $aTemp);
Then use the first query:
"SELECT * FROM accounts WHERE username IN ( $user )";
Edit: adding error checking:
$getlevel = $db->query("SELECT * FROM accounts WHERE username IN ( $user )");
if ($getlevel == false) {
// Normally you'll build into a function or class, but this is the simple example
// Never output SQL errors on a live site, but log to file or (if you can do it safely) the database.
echo 'Whoopsie<br />';
var_dump($db->errorInfo());
exit();
}
Using data binding with IN clauses is not that nice, so if you really need IN and don't care about using the old, deprecated mysql_* function, try this:
$user="'".implode("','",array_map(function($s){
return mysql_real_escape_string($s);
},$matches["user"])."'";
$rara="SELECT * FROM accounts WHERE username IN ($user)";
I have a submission script that I wrote in PHP. It is used by multiple surveys at our organization. The surveys are created by other users. When they submit to the script, PHP puts the data into the appropriate table in MySQL. The error that I run into sometimes is that the user(s) update the form. They add a field, or rename an input and the script doesn't account for it since it expects everything to be the same. So, I am trying to find a way to make it accomodate for when a new field is added. Here is what I have:
if( mysql_num_rows( mysql_query("SHOW TABLES LIKE '".$survey."'"))){
echo "table exists";
$sql = "SELECT * FROM " . $survey . ";";
$result = mysql_query($sql)
or die(mysql_error());
$i=0;
while($row = mysql_fetch_row($result));{
echo $row[0];
foreach($_POST as $k => $v){
$i++;
if($k != $row[$i]){
$query = "ALTER TABLE " . $survey . " ADD " . $k . " VARCHAR(100);";
mysql_query($query)
or die(mysql_error());
}
}
}
}
I am used to doing while loops in JS, so I don't know if using i works here (actually, I know it doesn't work... because it doesn't work...). What I am trying to say is that if a key doesn't match a current field name, then add it to the table. How can I return $row correctly?
When I submit to the script it says:
Duplicate column name 'V4'
I have echo $row[0] but it returns a 1. Which is the is the int used in the primary key for the for the first record.
You have a ; at the end of your while loop declaration that shouldn't be there. Not sure if that is causing the problem as you don't say what the above code does do. Update the question if the ; is not the issue.
Your while loop declaration should look like this: while($row = mysql_fetch_row($result)) {
Also, as Marc B so diplomatically put it in a comment to your question, you should be escaping any user input that goes directly into a query.
The easiest way to do this is to use $survey = mysql_real_escape_string($survey), before your first use of $survey, as a start or switch to PDO/MySQLi and use input binding (prepared statements). Here are the prepared statements docs for PDO. More can, and should, be done to protect yourself, but the above is a good start.
<?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 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:
Get a list of all images
Delete an image from the gallery
Upload an image to the gallery
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 1
deleteImage.php?image=46 will delete the image with ID 46
uploadImage.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.
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.
HI
I am getting Resource#6 and Resource#7 when I print the following variables:
$salty_password = sha1($row['salt'], $_POST['password']);
if(isset($_POST['subSignIn']) && !empty($_POST['email']) && !empty($_POST['password'])) {
$query = "SELECT `salt` FROM `cysticUsers` WHERE `Email` = '" . $_POST['email'] . "'";
$request = mysql_query($query,$connection) or die(mysql_error());
$result = mysql_fetch_array($request);
$query2 = "SELECT * FROM `cysticUsers` WHERE `Email` = '". $_POST['email']."' AND `Password` = '$salty_password'";
$request2 = mysql_query($query2,$connection) or die(mysql_error());
$result = mysql_fetch_array($request2);
print_r($request);
print_r($request2);
if(#mysql_num_rows($request,$request2)) {
$_SESSION['CLIFE']['AUTH'] = true;
$_SESSION['CLIFE']['ID'] = $result['id'];
// UPDATE LAST ACTIVITY FOR USER
$query = "UPDATE `cysticUsers` SET `LastActivity` = '" . date("Y-m-d") . " " . date("g:i:s") . "' WHERE `id` = '" . mysql_real_escape_string($_SESSION['CLIFE']['ID']) . "' LIMIT 1";
mysql_query($query,$connection);
if(!empty($_POST['return'])) {
header("Location: " . $_POST['return']);
}else{
header("Location: CysticLife-Dashboard.php?id=" . $_SESSION['CLIFE']['ID']);
}
}
}else{
$_SESSION['CLIFE']['AUTH'] = false;
$_SESSION['CLIFE']['ID'] = false;
}
?>
Trying to troubleshoot this code chunk and not sure what that means. I am trying to sign back in with the clear text password I signed up with after its been hashed and salted. I feel like I'm very close but something is slightly wrong. Help on why that is not working would be greatly appreciated as well.
Thanks in advance
mysql_query() returns result sets as objects of type resource (they're not objects in terms of PHP OOP code but I can't think of a better word). These contain binary data that can only be read by certain functions, for example the mysql_fetch_*() functions.
To debug your MySQL queries you should check for errors using mysql_error() and mysql_errno() and/or save your SQL statements in variables and print those.
From what I see, you're performing two queries but overwriting the same $result variable, without doing anything about the first. Also, mysql_num_rows() can only count one result set at a time, so you can't pass two result sets into the same call.
Those are PHP's internal data types called resource.
They cannot be serialized (i.e. there's no "toString()") and are hence displayed as Resource#X.
SQL queries through PHP are done using a variable known as a resource. This variable, in your code, is completely useless other than to pass to each function you want to perform (i.e. change a database, execute a query, grab the last error, etc.).
That being said, executing a query doesn't return any information from the database, just a reference to that record set (where in PHP it's storing the information). You would then use that variable in a call such as mysql_fetch_array to retrieve the actual row information.