So I've created a simple PHP function to 'UPDATE' my MySQL row just by updating the array that is received by PHP.
function db_updateproduct(array $new, array $old = array()) {
$diff = array_diff($new, $old);
return 'INSERT INTO table (`'.mysql_real_escape_string(implode(array_keys($diff), '`,`')).'`) VALUES \''.mysql_real_escape_string(implode(array_values($diff), '\',\'')).'\'';
}
...
Update (with Accepted answer)
function db_updateproduct(array $new, array $old = array()) {
$diff = array_diff($new, $old);
return 'INSERT INTO `Product` (`'.implode(array_keys($diff), '`,`').'`) VALUES (\''
.implode(array_map('mysql_real_escape_string', array_values($diff)), '\', \'').'\')';
}
Now...
echo db_updateproduct(array('a' => 'on\'e', 'b' => 'two', 'c' => 'three'));
returns:
INSERT INTO `Product` (`a`,`b`,`c`) VALUES ('on\'e', 'two', 'three')
(As expected/wanted!)
You can run the escape function on the keys and values with array_map():
$escaped_keys = array_map('mysql_real_escape_string', array_keys($diff));
$escaped_values = array_map('mysql_real_escape_string', array_values($diff));
Then you can do your implode() magic on these two arrays.
UPDATE: As #YourCommonSense correctly pointed it out, it does not really make sense to run mysql_real_escape_string() on values that will be used in the query as field names/table names/etc. It correctly escapes \x00, \n, \r, \, ', " and \x1a, but it does NOT escape the backtick, so the query is still vulnerable to attacks.
You should validate the field names (so only the expected names can be used) or even better, use prepared queries (I recommend PDO).
Suggested reading:
Are mysql_real_escape_string() and mysql_escape_string() sufficient for app security?
As a matter of fact, doing mysql_real_escape_string on the array keys is an example of absolutely useless action.
Related
Background..
This is user input being collected so I need to expect some strange stuff and try to fix up string before passing these into functions. User input is stored into the database similar to so:
{"value":"O'Neil,'Smith',\"O'Reilly\",100"}
So the script pulls these out of the database, json_decodes them, and then now I'm trying to fix those value strings up. Here's the best example of that I can give.
$json = '{"value":"O\'Neil,\'Smith\',\"O\'Reilly\",100"}';
$array = json_decode($json, true);
The Goal..
How could I go about escaping quotes in strings like so:
O'Neil,Smith,O'Reilly,100
"O'Neil","Smith","O'Reilly",100
'O'Neil','Smith','O'Reilly',100
O'Neil,'Smith',"O'Reilly",100
So that I get the following result out of each:
'O\'Neil','Smith','O\'Reilly',100
Values may or may not contain commas. It could just be a single value like O'Neil or 100.
I'm pretty sure preg_replace could so something like this, or even preg_replace_callback, but I'm just not sure how to go about this.
The below do not work at all but I'm thinking one of these approaches should work.
$value = preg_replace('/(.*?)/', '$1', $array['value']);
$value = preg_replace_callback('/(.*?)/', 'addslashes', $array['value']);
I've also tried exploding the strings using the commas and looping the values but that escapes the quotes I don't want to touch as well.
Thanks all!
I think this function will do what you want. It uses preg_match_all to find either a quoted string (single or double, possibly with escaped quotes inside), or a set of non-comma characters. Each of those values is then trimmed of quotes, and any non-escaped single quotes are replaced with escaped ones. Finally non-numeric values are placed into single quotes:
function quote($value) {
preg_match_all('/"(?:\\\\"|[^"])*"|\'(?:\\\\\'|[^\'])*\'|[^,]+/', $value, $values);
foreach ($values[0] as &$value) {
$value = trim($value, "'\"");
$value = preg_replace("/(?<!\\\\)'/", "\\'", $value);
if (!is_numeric($value)) $value = "'$value'";
}
return implode(',', $values[0]);
}
To use with your sample strings:
echo quote("O'Neil,Smith,O'Reilly,100") . PHP_EOL;
echo quote("\"O'Neil\",\"Smith\",\"O'Reilly\",100") . PHP_EOL;
echo quote("'O\'Neil','Smith','O\'Reilly',100") . PHP_EOL;
echo quote("O'Neil,'Smith',\"O'Reilly\",100") . PHP_EOL;
Output:
'O\'Neil','Smith','O\'Reilly',100
'O\'Neil','Smith','O\'Reilly',100
'O\'Neil','Smith','O\'Reilly',100
'O\'Neil','Smith','O\'Reilly',100
Demo on 3v4l.org
I have this problem:
One string like:
$stringa = "array('name' => 'John')"
I want obtain : array('name' => 'John') for use in my code like array
Any helps? Thanks
Caution using eval...
Caution The eval() language construct is very dangerous because it
allows execution of arbitrary PHP code. Its use thus is discouraged.
If you have carefully verified that there is no other option than to
use this construct, pay special attention not to pass any user
provided data into it without properly validating it beforehand.
<?php
$stringa = "array('name' => 'John')";
$code = "\$a = " . $stringa . ";";
eval($code);
print_r($a);
Gouda Elalfy was on the right idea, but his solution simply made the whole string a single array value.
First, we need to remove the excess datatype information:
$slimString = substr($stringa,6);
$slimString = rtrim($slimString,")");
This now gives us a string of:
'name' => 'John'
So then the Keys and the values in the string need to be split up,
so break at => as so:
For multiple values in the string:
This method also includes the single quotes to limit catching punctuation commas (please note this method was screwed up by trim not being as effective as I'd have liked and requiring str_replace quotes instead).
$slimString = str_replace("', '","=>", $slimString);
Then
$slimStringParts = explode("=>", $slimString);
This will split on => (or ,) so that multiple values of array contents can be generated.
Then cycle through each of the array pieces, on the basis that the EVEN numbers (and zero) are the Keys and the ODD numbers are the values, also removing the quotes as well for each one,
I was originally using trim but for some reason trim was not working as fully as I expected. so instead reverted to st_replace
foreach($slimStringParts as $key => $part){
if(($key%2) == 0){
$part = str_replace("'","",$part);
$arrayOutput[$part] = str_replace("'","",$slimStringParts[$key+1]);
}
}
unset($key, $part);
The foreach only acts upon the even and zero values as referenced above, and they take the original key value + 1 as their contents.
The trim/str_replace removes the single quotes and looks untidy but this works for a string of one or more array values
And finally the Output:
print_r($arrayOutput);
Test with the original:
input : "array('name' => 'John')"
Output : Array ( [name ] => John )
Tested with a multivalue array string:
input : "array('name' => 'John', 'surname' => 'James', 'Ride' => 'horse')"
Output : Array ( [name ] => John [surname ] => James [Ride ] => horse )
Full code:
$stringa = "array('name' => 'John')";
$stringb = "array('name' => 'John', 'surname' => 'James', 'Ride' => 'horse')";
$slimString = substr($stringb,6);
$slimString = rtrim($slimString,")");
$slimString = str_replace("', '","=>", $slimString);
$slimStringParts = explode("=>", $slimString);
foreach($slimStringParts as $key => $part){
if(($key%2) == 0){
$part = str_replace("'","",$part);
$arrayOutput[$part] = str_replace("'","",$slimStringParts[$key+1]);
}
}
unset($key,$part);
print_r($arrayOutput);
exit;
Please note my trim() idea was what I wanted but that seems to be influenced by my page character encoding :-(
You can use eval() function.
But it's not recommanded.
http://php.net/manual/en/function.eval.php
Eval is unsafe, try to use it as temporary method and find a better solution.
Example :
$stringa="array('name' => 'John');";
$array = eval($stringa);
I am working on a project that uses OSCommerce with MySQL and I'm confused as to when I should use tep_db_input() or tep_db_prepare_input(). I'd assume I should use tep_db_input() around any strings that are being inserted/updated, but then when should the other function be used?
For example, if I were to SELECT some data from the database, and use the result to then INSERT a row into another table, do I need to prepare the input at some point? Or just use tep_db_input again?
$width = '3"'; // 3 inches
$new_height = '3\' 5"'; // 3 feet 5 inches
$result = tep_db_query(
"SELECT height
FROM measurements
WHERE width = '".tep_db_input($width)."'"
);
while ($row = tep_db_fetch_array($result)) {
tep_db_query(
"INSERT INTO measurement_history (
field,
old_value,
new_value
) VALUES (
'height',
'".tep_db_input($row['height'])."',
'".tep_db_input($new_height)."'
)"
);
}
Is this correct?
Edit:: In case anyone isn't familiar with those functions, here are their definitions:
function tep_sanitize_string($string) {
$patterns = array ('/ +/','/[<>]/');
$replace = array (' ', '_');
return preg_replace($patterns, $replace, trim($string));
}
function tep_db_input($string, $link = 'db_link') {
global $$link;
if (function_exists('mysql_real_escape_string')) {
return mysql_real_escape_string($string, $$link);
} elseif (function_exists('mysql_escape_string')) {
return mysql_escape_string($string);
}
return addslashes($string);
}
function tep_db_prepare_input($string) {
if (is_string($string)) {
return trim(tep_sanitize_string(stripslashes($string)));
} elseif (is_array($string)) {
reset($string);
while (list($key, $value) = each($string)) {
$string[$key] = tep_db_prepare_input($value);
}
return $string;
} else {
return $string;
}
}
tep_db_input uses mysql_real_escape_string or mysql_escape_string and that's a recommended way to prepare your database input.
(And I guess this function will use mysqli_real_escape_string() or similiar in a later release since mysql_real_escape_string will be deprecated starting with PHP 5.5.0. )
Where tep_db_input with mysql_real_escape_string just does escaping:
mysql_real_escape_string() calls MySQL's library function mysql_real_escape_string,
which prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a.
tep_db_prepare_input does different things like trimming whitespaces and replacing brackets and unquoting(!) by calling stripslashes.
So my advice would be: always use tep_db_input. And if you use tep_db_prepare_input to get rid of whitespace etc. use tep_db_input afterwards, too.
This is a bit weird, but you use both. Doing it this way will prevent attacks from malicious users, as well as unintended problems from unusual inputs.
Use tep_db_prepare input on any input data from the HTML form. This clears up issues with HTML, magic quotes, and script injections. Do not use this on text retrieved from the database.
Then you use tep_db_input before writing it to the database. This will escape the MySQL characters to prevent SQL injection attacks and other such problems.
Here's a code sample that shows it:
$clean = tep_db_prepare_input($_POST['name']);
$query_text = tep_db_query("select * from " . TABLE_NAME . " where name='" . tep_db_input($clean) . "'");
I have a dilemma how should I mysql_real_escape_string() my variables without inserting them into the database \n, \r, \x00 when someone uses " ' or <br> on my comment field, I tried with preg_replace instead of mysql_real_escape_string, but seems I don't know exactly how to allow all the chars and signs I want.
mysql_real_escape_string only escapes values so that your queries don't break, it also protects against SQL injection if used correctly.
If you don't want certain characters you will need to use additional functions to strip them before you apply mysql_real_escape_string.
[insert obligatory "use prepared statements" comment]
Ex:
$string = "My name is
John";
$filtered_string = str_replace("\n", " ", $string); // filter
$escaped = mysql_real_escape_string($filtered_string); // sql escape
mysql_query("INSERT INTO `messages` SET `message` = '" . $escaped . "'");
You should be able to use str_replace to help with this:
mysql_real_escape_string(str_replace(array("\n", "\r\n", "\x00", '"', '\''), '', $input));
Having said that, it is a good idea to switch to mysqli or PDO for database read / write. Both of these allow prepared statements, which reduce the risk of SQL injections.
Here's an example of PDO:
$stmt = $PDOConnection->prepare('INSERT INTO example_table (input_field) VALUES (:input_field)');
$stmt->bindParam(':input_field', $input);
$stmt->execute();
A problem I recently ran into was that when trying to update a field in my database using this code would not work. I traced it back to having a % sign in the text being updated ($note, then $note_escaped)... Inserting it with sprintf worked fine though.
Should I not be using sprintf for updates, or should it be formed differently?
I did some searching but couldn't come up with anything.
$id = mysql_real_escape_string($id);
$note_escaped = mysql_real_escape_string($note);
$editedby = mysql_real_escape_string($author);
$editdate = mysql_real_escape_string($date);
//insert info from form into database
$query= sprintf("UPDATE notes_$suffix SET note='$note_escaped', editedby='$editedby', editdate='$editdate' WHERE id='$id' LIMIT 1");
You are using sprintf totally wrong. Removing the function call in your code would still do the same thing. It should be:
sprintf("UPDATE notes_%s SET note='%s', editedby='%s', editdate='%s' WHERE id=%d LIMIT 1", $suffix, $note_escaped, $editedby, $editdate, $id);
You should read the manual.
first of all you should be using prepared statements instead of a sprintf-call
but if you absolutely have to do it this way you have to use:
$id = mysql_real_escape_string($id);
$note_escaped = mysql_real_escape_string($note);
$editedby = mysql_real_escape_string($author);
$editdate = mysql_real_escape_string($date);
//insert info from form into database
$query= sprintf("
UPDATE notes_%s /* this is still open for injection, and cannot be properly escaped with mysql_real_escape_string */
SET note='%s',
editedby='%s',
editdate='%s'
WHERE id='%d'
LIMIT 1",
$suffix,
$note_escaped, $editedby, $editdate, $id);
You can escape the % in the source text by replacing it with \% in mysql.
sprintf() is not used much in PHP, unless you need to format data somehow. These two statements work identically in PHP:
$num = 42;
$char = 'q';
$text = sprintf('The number is %d and the character is %s', $num, $char);
$text = "The number is $num and the character is $char";
sprintf's used more in C for "printing" variable data into a string. But PHP can already do that with double-quoted strings, so unless you need to use sprintf's special formatting functions (e.g. %0.2f for a 2-decimal-place float), it's easier to use the regular string method.
From http://php.net/manual/en/function.mysql-real-escape-string.php:
Note: mysql_real_escape_string() does not escape % and _. These are wildcards in MySQL if combined with LIKE, GRANT, or REVOKE.
You need to manually escape the % and _ if any with \% and _. I don't recommend using sprintf, but just improving your escape function.