I'm terribly bad at keeping MySQL queries straight, but that aside I have one query working for some data input, but not all. My guess is quotation marks getting escaped where they should be.
I have the entire query string get escaped at the same time. Is this bad practice or does it really matter?
Here's the query:
"INSERT INTO bio_manager_pubs(userid,category,citation,date,link,requests) VALUES ( ".
$userid.",'".
$_POST['category']."', '".
htmlentities($_POST['pub'])."',
FROM_UNIXTIME(".strtotime($_POST['date'])."),'".
$_POST['link']."',
0)"
In query:
Userid and requests are ints
Link and Category are Tiny Text (not sure if that's appropriate, but max is 255 char, so would VarChar be better?)
Date is a date (is it better to reformat with php or reformat with mysql?)
Citation is a text field
Any ideas?
Thanks
EDIT:
The answer to this question was posted four times there abouts where the issue was me escaping the entire query.
What was left out, and cause some confusion was the code surrounding the query.
It was like this
$db->query($query)
This where the function query was:
public function query($SQL)
{
$this->SQL = $this->mysqli->real_escape_string($SQL);
$this->result = $this->mysqli->query($SQL);
if ($this->result == true)
{
return true;
}
else
{
printf("<b>Problem with SQL:</b> %s\n", $this->SQL);
exit;
}
}
I just found a class that made life a bit simpler on smaller projects and stuck with it. Now, the issue I'm running into is removing $this->mysqli->real_escape_string($SQL); and adding in escapes elsewhere in the code.
I really don't see any sanitizing of your $_POST data, and there is really no need to run htmlentities before you insert into the database, that should be done when you take that data and display it on the page. Make sure to sanitize your posts!! Using mysql_real_escape_string() or preferably PDO with prepared statements.
If you are running mysql_real_escape_string() on this whole query, after you build it, than that is what is breaking it.
Use it on the individual posts, and / or cast variables that should only ever be numbers to integers.
Heres what I would change it to in your case:
$posted = $_POST;
foreach($posted as &$value)
$value = mysql_real_escape_string($value);
$date = strtotime($posted['date']);
$q = "INSERT INTO bio_manager_pubs(userid,category,citation,date,link,requests) VALUES
(
'{$userid}',
'{$posted['category']}',
'{$posted['pub'])}',
FROM_UNIXTIME({$posted['date']}),
'{$posted['link']}',
'0'
)";
I believe it is considered bad practice to build the entire query and then escape the whole thing. You should sanitize the inputs as soon as they enter the code, not after you've started using them to build your database interactions.
You'd want to sanitize each input, kind of like this:
$category = mysql_real_escape_string($_POST['category'])
And then you'd use the local variables, not the inputs, to build your SQL command(s).
Also, you may want to look into something like PDO for your data access, which manages a lot of the details for you.
I think you need to wrap each of your inputs in mysql_real_escape_string (only once!), not the whole query. Other than that it looks OK to me.
"INSERT INTO bio_manager_pubs(userid,category,citation,date,link,requests) VALUES ( ".
mysql_real_escape_string($userid).",'".
mysql_real_escape_string($_POST['category'])."', '".
mysql_real_escape_string(htmlentities($_POST['pub']))."',
FROM_UNIXTIME(".mysql_real_escape_string(strtotime($_POST['date']))."),'".
mysql_real_escape_string($_POST['link'])."',
0)"
Instead of escaping the entire SQL query (which can run the risk of breaking things), just escape the user's input:
$userid = mysql_real_escape_string($userid);
$cat = mysql_real_escape_string($_POST['category']);
$pub = mysql_real_escape_string($_POST['pub']);
$date = strtotime($_POST['date']);
$link = mysql_real_escape_string($_POST['link']);
$query = "INSERT INTO bio_manager_pubs(userid, category, citation, date, link, requests)"
." VALUES ($userid, '$cat', '$pub', $date, '$link', 0 );";
Well for a start you should avoid using data from external sources directly in a query, so I would rewrite the code so as not to use $_POST in your query. Even better if you can to use PDO or similar to escape your data. And I would avoid converting text with htmlentities before inserting it into your database. You're better off doing that after you pull it from the database as you will then be able to use that data in other (non-HTML) output contexts.
But in terms of inline code, do you have magic_quotes on?
Try something like this
if (get_magic_quotes_gpc()) {
$category = stripslashes($_POST['category']);
$pub = stripslashes($_POST['pub']);
$link = stripslashes($_POST['link']);
} else {
$category = $_POST['category'];
$category = $_POST['category'];
$category = $_POST['category'];
}
$category = mysql_escape_string( $category );
$pub = mysql_escape_string( $pub );
$link = mysql_escape_string( $link );
$sql = "
INSERT INTO bio_manager_pubs(userid,category,citation,date,link,requests) VALUES (
". $userid.",
'$category',
'$pub',
FROM_UNIXTIME(".strtotime($_POST['date'])."),
'$link',
0
)";
Turn off magic_quotes_gpc and use prepared statements.
With magic_quotes_gpc disabled, you don't end up with automatic escaping of input - and magic_quotes_gpc is deprecated anyway.
Use parameter binding prepared statements to avoid SQL injection rather than escaping characters. I personally suggest using PDO or MDB2 to talk to your db, but you can also do prepared statements with the mysqli driver. Note that the mysql driver is on the chopping block as well, so you soon will be forced to either use mysqli or an abstraction layer like MDB2.
I bet though that magic_quotes_gpc is your problem.
Related
I am having an issue with this not running at all when the information is submitted, I believe that I have the syntax wrong.
if ($_POST['note'] != $player->note_text) {
$message = 'Admin '.$user.' has added the note ('.$_POST['note'].') to '.$player->name.'('.$pid.')';
logIt($user, $message, $dbcon);
$note = $_POST['note'];
$note = '"'.$note.'"';
$UpdateN = "INSERT INTO notes (uid, staff_name, name, alias, note_text, warning) VALUES ('$_POST[hidden]', '$user', '$player->name', '$player->aliases', '$note','$_POST[warn]')";
$UpdateN2 = "INSERT INTO players WHERE `playerid` = $_POST[hidden] (warning) VALUES ('$_POST[warn]')";
mysqli_query($dbcon, $UpdateN, $UpdateN2);
The new line I added which seems to have broken it is '$UpdateN2'.
I am not sure if the new line has broken the statement, since I am new to PHP and mysqli any pointers to forums or websites that I can learn and understand this in a more detailed way I would appreciate.
Edit: I have since moved from using mysqli to PDO, I strongly suggestion that anyone willing to start using MYSQL commands with PHP to have a look at this: http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers
I started reading this and testing things out, I find this much more easier but also it looks a lot cleaner and understandable when reading it back after a few days have passed.
In keeping with the mysqli procedural style in the original, using prepared statements with bind placeholders, something like this:
$UpdateN = 'INSERT INTO notes (uid, staff_name, name, alias, note_text, warning)'
. ' VALUES ( ?, ? , ? , ? , ? , ? )';
$UpdateN2 = 'UPDATE players SET warning = ? WHERE playerid = ? ';
if( $sth = mysqli_prepare($dbcon,$UpdateN) ) {
mysqli_stmt_bind_param($sth,'ssssss'
,$_POST['hidden']
,$user
,$player->name
,$player->aliases
,$_POST['note']
,$_POST['warn']
);
if( mysqli_stmt_execute($sth) ) {
// statement execution successful
} else {
printf("Error: %s\n",mysqli_stmt_error($sth));
}
} else {
printf("Error: %s\n",mysqli_error($dbcon));
}
if( $sth2 = mysqli_prepare($dbcon,$UpdateN2) ) {
mysqli_stmt_bind_param($sth2,'ss'
,$_POST['warn']
,$_POST['hidden']
);
if( mysqli_stmt_execute($sth2) ) {
// statement execution successful
} else {
printf("Error: %s\n",mysqli_stmt_error($sth2));
}
} else {
printf("Error: %s\n",mysqli_error($dbcon));
}
If it was me, I'd just make two separate calls to mysqli_query, one for each of your separate queries. PHP has historically been very wary of permitting multiple queries in a single function call, because of possible sql injection vulnerabilities.
On a related note, you need to sanitize your input. $_POST should never, ever, ever appear directly in a mysql query string, because your post data might -actually be something like ')"; DROP TABLE users;'.
Finally, you're using a WHERE clause on an insert. That's probably breaking your query. You should take a couple of days and research how and why that's breaking your query, and how and why to write proper sql queries. Doing so will add value to your company, because your company is less likely to suffer a catastrophic data breach, and it will add value to you, because you'll be a better SQL coder.
Edit: and in the time it took me to write this, three different people made each of those points. LOL.
I have been using the block of code below to supposedly stop sql injections. It is something someone showed me when I first started php(which was not that long ago)
I place it in every page just as shown on the open. I am wondering if it is effective? I do not know how to test for sql injections
<?php
//Start the session
session_start();
//=======================open connection
include ('lib/dbconfig.php');
//===============This stops SQL Injection in POST vars
foreach ($_POST as $key => $value) {
$_POST[$key] = mysql_real_escape_string($value);
}
foreach ($_GET as $key => $value) {
$_GET[$key] = mysql_real_escape_string($value);
}
My typical insert and update queries look like this
$insert = ("'$email','$pw','$company', '$co_description', '$categroy', '$url', '$street', '$suite', '$city', '$state', '$zip', '$phone', '$date', '$actkey'");
mysql_query("INSERT INTO provider (email, pw, company, co_description, category, url, street, suite, city, state, zip, phone, regdate, actkey) VALUES ($insert)") or die ('error ' . mysql_error());
mysql_query("UPDATE coupon SET head='$_POST[head]', fineprint='$_POST[fineprint]', exdate='$exdate', creationdate=NOW() WHERE id='$cid'") or die ('error ' . mysql_error());
That's somewhat effective, but it's suboptimal -- not all of the data you receive in _GET and _POST will go into the database. Sometimes you might want to display it on the page instead, in which case mysql_real_escape_string can only hurt (instead, you'd want htmlentities).
My rule of thumb is to only escape something immediately before putting it into the context in which it needs to be escaped.
In this context, you'd be better of just using parameterized queries -- then escaping is done for you automatically.
This is not enough.
1. You're missing cookies, $_COOKIE variable.
2. If you use $_REQUEST you're in trouble.
3. You didn't show your queries, you must enquote each variable with single quotes '' when you put it into query (especiall when the data is supposted to be an integer and you might think that quote is not necessary in that case, but that would be a big mistake).
4. Data used in your query could come from other source.
The best way is to use data binding and have the data escaped automatically by the driver, this is available in PDO extension.
Example code:
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1' $user, $password);
$stmt = $PDO->prepare("SELECT * FROM test WHERE id=? AND cat=?");
$stmt->execute(array($_GET["id"], $_GET["cat"]));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
You can also bind data using string keys:
$stmt = $PDO->prepare("SELECT * FROM test WHERE id = :id AND cat = :cat");
$stmt->execute(array(":id" => $_GET["id"], ":cat" => $_GET["cat"]));
If you want to learn PDO, you might find useful these helper functions I use:
http://www.gosu.pl/var/PDO.txt
PDO_Connect(dsn, user, passwd) - connects and sets error handling.
PDO_Execute(query [, params]) - only execute query, do not fetch any data.
PDO_InsertId() - last insert id.
PDO_FetchOne(query [, params]) - fetch 1 value, $count = PDO_FetchOne("SELECT COUNT(*) ..");
PDO_FetchRow(query [, params]) - fetch 1 row.
PDO_FetchAll(query [, params]) - fetch all rows.
PDO_FetchAssoc(query [, params]) - returns an associative array, when you need 1 or 2 cols
1) $names = PDO_FetchAssoc("SELECT name FROM table");
the returned array is: array(name, name, ...)
2) $assoc = PDO_FetchAssoc("SELECT id, name FROM table")
the returned array is: array(id=> name, id=>name, ...)
3) $assoc = PDO_FetchAssoc("SELECT id, name, other FROM table");
the returned array is: array(id=> array(id=>'',name=>'',other=>''), id=>array(..), ..)
Each of functions that fetch data accept as 2nd argument parameters array (which is optional), used for automatic data binding against sql injections. Use of it has been presented earlier in this post.
Kind of.
The mysql_real_escape_string function takes the given variable and escapes it for SQL queries. So you can safely append the string into a query like
$safe = mysql_real_escape_string($unsafe_string);
$query = 'SELECT * FROM MyTable WHERE Name LIKE "' . $safe . '" LIMIT 1';
It does NOT protect you against someone putting malicious code into that query to be displayed later (i.e. XSS or similar attack). So if someone sets a variable to be
// $unsafe_string = '<script src="http://dangerous.org/script.js"></script>'
$safe = mysql_real_escape_string($unsafe_string);
$query = 'UPDATE MyTable SET Name = "' . $safe . '"';
That query will execute as you expect, but now on any page where you print this guy's name, his script will execute.
This is completely WRONG approach.
In fact, you are mimicking infamous magic quotes, which is acknowledged as a bad practice. With all it's faults and dangers.
To help you understand why your initial way was wrong Magic quotes in PHP
To help you understand why escaping has nothing to do with "data safety" yet not sufficient to protect your query: Replacing mysql_* functions with PDO and prepared statements
To help you understand when prepared statements not sufficient either and what to do in these cases: In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
this is not to prevent SQL Injection the real escape method only add \ to the dangerous
characters like " or ' so a string with "hi"do'like" will become "hi\"do\'like\" so it is
less dangerous
this method is not always usefull ; in case you want to display the content of tha escaped
variable in a page it will only destroy it and make it less readable
I have field called filter1 on a form, I would like to be able to save quoted text into mysql. So I would like to be able to save the value "foo bar"...instead its saving just /
Here is what I have:
$keyword1 = mysql_real_escape_string($_POST['filter1']);
Any help is appreciated.
Here is how I construct the query
$keyword1 = mysql_real_escape_string($_POST['filter1']);
$keyword2 = $_POST['filter2'];//."|".$_POST['filterby'];
$keyword3 = $_POST['filter3'];//."|".$_POST['filterby2'];
$urlfilter1 = $_POST['url1'];
$urlfilter2 = $_POST['url2'];//."|".$_POST['url_filter'];
$urlfilter3 = $_POST['url3'];//."|".$_POST['url_filter2'];
//echo "combo_id:".$num." <BR></br>";
//echo "status:".$status." <BR></br>";
//echo "saveQuery:".$saveQuery." <BR></br>";
//$myFilter = "save";
$insert_query = sprintf("UPDATE COMBINATION
SET STATUS_ID=%s, QUERY=\"%s\",
KEYWORD1=\"%s\", KEYWORD2=\"%s\", KEYWORD3=\"%s\",
URLFILTER1=\"%s\", URLFILTER2=\"%s\", URLFILTER3=\"%s\"
WHERE COMBINATION_ID=%s",$status,$saveQuery,
$keyword1,$keyword2,$keyword3,
$urlfilter1,$urlfilter2,$urlfilter3,
$num);
//echo "insert_query:".$insert_query." <BR></br>";
$result = mysql_query($insert_query) or die(mysql_error());
if($result)
{
echo "Saved successfully<br>";
}
}
?>
Unless you have a very old and restricted environment, use PDO. It will save you buckets of sweat and tears. With PDO it is very easy to escape input and avoid SQL injection attacks, which is illustrated in the answer that this link leads to.
Well first you need to connect to the database with mysql_connect() http://php.net/manual/en/function.mysql-connect.php
Then you need to call your INSERT query with mysql_query() http://php.net/manual/en/function.mysql-query.php
By the way, you are doing the right thing by escaping the string before putting it into a query, well done :)
For some reason you are escaping only one variable, while adding to the query several of them.
Why don't you escape them all?
However, your problem may be somewhere else.
What is $saveQuery I am curious?
In PHP, I want to insert into a database using data contained in a associative array of field/value pairs.
Example:
$_fields = array('field1'=>'value1','field2'=>'value2','field3'=>'value3');
The resulting SQL insert should look as follows:
INSERT INTO table (field1,field2,field3) VALUES ('value1','value2','value3');
I have come up with the following PHP one-liner:
mysql_query("INSERT INTO table (".implode(',',array_keys($_fields)).") VALUES (".implode(',',array_values($_fields)).")");
It separates the keys and values of the the associative array and implodes to generate a comma-separated string . The problem is that it does not escape or quote the values that were inserted into the database. To illustrate the danger, Imagine if $_fields contained the following:
$_fields = array('field1'=>"naustyvalue); drop table members; --");
The following SQL would be generated:
INSERT INTO table (field1) VALUES (naustyvalue); drop table members; --;
Luckily, multiple queries are not supported, nevertheless quoting and escaping are essential to prevent SQL injection vulnerabilities.
How do you write your PHP Mysql Inserts?
Note: PDO or mysqli prepared queries aren't currently an option for me because the codebase already uses mysql extensively - a change is planned but it'd take alot of resources to convert?
The only thing i would change would be to use sprintf for readability purposes
$sql = sprintf(
'INSERT INTO table (%s) VALUES ("%s")',
implode(',',array_keys($_fields)),
implode('","',array_values($_fields))
);
mysql_query($sql);
and make sure the values are escaped.
Nothing wrong with that. I do the same.
But make sure you mysql_escape() and quote the values you stick in the query, otherwise you're looking at SQL injection vulnerability.
Alternately, you could use parametrized queries, in which case you can practically pass the array in itself, instead of building a query string.
The best practice is either to use an ORM (Doctrine 2.0), an ActiveRecord implementation (Doctrine 1.0, RedBean), or a TableGateway pattern implementation (Zend_Db_Table, Propel). These tools will make your life a lot easier, and handle a lot of the heavy lifting for you, and can help protect you from SQL injections.
Other than that, there's nothing inherently wrong with what you're doing, you just might want to abstract it away into a class or a function, so that you can repeat the functionality in different places.
Using the sprintf trick mentioned by Galen in a previous answer, I have come up with the following code:
$escapedfieldValues = array_map(create_function('$e', 'return mysql_real_escape_string(((get_magic_quotes_gpc()) ? stripslashes($e) : $e));'), array_values($_fields));
$sql = sprintf('INSERT INTO table (%s) VALUES ("%s")', implode(',',array_keys($_fields)), implode('"," ',$escapedfieldValues));
mysql_query($sql);
It generates a escaped and quoted insert. It also copes independent of whether magic_quotes_gpc is on or off. The code could be nicer if I used new PHP v5.3.0 anonymous functions but I need it to run on older PHP installations.
This code is a bit longer that the original (and slower) but it is more secure.
I use this to retrieve the VALUES part of the INSERT.
But it might be an absurd way to do things. Comments/suggestions are welcome.
function arrayToSqlValues($array)
{
$sql = "";
foreach($array as $val)
{
//adding value
if($val === NULL)
$sql .= "NULL";
else
/*
useless piece of code see comments
if($val === FALSE)
$sql .= "FALSE";
else
*/
$sql .= "'" . addslashes($val) . "'";
$sql .= ", ";
};
return "VALUES(" . rtrim($sql, " ,") . ")";
}
There is a problem with NULL (in the accepted answer) values being converted to empty string "". So this is fix, NULL becomes NULL without quotes:
function implode_sql_values($vals)
{
$s = '';
foreach ($vals as $v)
$s .= ','.(($v===NULL)?'NULL':'"'.mysql_real_escape_string($v).'"');
return substr($s, 1);
}
Usage:
implode_sql_values(array_values( array('id'=>1, 'nick'=>'bla', 'fbid'=>NULL) ));
// =='"1","bla",NULL'
If you want to enhance your approach and add the possibility for input validation and sanitation, you might want to do this:
function insertarray($table, $arr){
foreach($arr as $k => $v){
$col[] = sanitize($k);
$val[] = "'".sanitize($v)."'";
}
query('INSERT INTO '.sanitize($table).' ('.implode(', ', $col).') VALUES ('.implode(', ', $val).')' );
}
Should be a simple question, I'm just not familiar with PHP syntax and I am wondering if the following code is safe from SQL injection attacks?:
private function _getAllIngredients($animal = null, $type = null) {
$ingredients = null;
if($animal != null && $type != null) {
$query = 'SELECT id, name, brief_description, description,
food_type, ingredient_type, image, price,
created_on, updated_on
FROM ingredient
WHERE food_type = \'' . $animal . '\'
AND ingredient_type =\'' . $type . '\';';
$rows = $this->query($query);
if(count($rows) > 0) {
etc, etc, etc
I've googled around a bit and it seems that injection safe code seems to look different than the WHERE food_type = \'' . $animal . '\' syntax used here.
Sorry, I don't know what version of PHP or MySQL that is being used here, or if any 3rd party libraries are being used, can anyone with expertise offer any input?
UPDATE
What purpose does the \ serve in the statement?:
WHERE food_type = \'' . $animal . '\'
In my googling, I came across many references to mysql_real_escape_string...is this a function to protect from SQL Injection and other nastiness?
The class declaration is:
class DCIngredient extends SSDataController
So is it conceivable that mysql_real_escape_string is included in there?
Should I be asking to see the implementation of SSDataController?
Yes this code is vulnerable to SQL-Injection.
The "\" escapse only the quote character, otherwise PHP thinks the quote will end your (sql-)string.
Also as you deliver the whole SQL-String to the SSDataControler Class, it is not possible anymore to avoid the attack, if a prepared string has been injected.
So the class SSDataControler is broken (vulnerable) by design.
try something more safe like this:
$db_connection = new mysqli("host", "user", "pass", "db");
$statement = $db_connection->prepare("SELECT id, name, brief_description, description,
food_type, ingredient_type, image, price,
created_on, updated_on
FROM ingredient
WHERE food_type = ?
AND ingredient_type = ?;';");
$statement->bind_param("s", $animal);
$statement->bind_param("s", $type);
$statement->execute();
by using the bind method, you can specify the type of your parameter(s for string, i for integer, etc) and you will never think about sql injection again
You might as well use mysql_real_escape_string anyway to get rid of anything that could do a drop table, or execute any other arbitrary code.
It doesn't matter where in the SQL statement you put the values, at any point it can have a ';' in it, which immediately ends the statement and starts a new one, which means the hacker can do almost anything he wants.
To be safe, just wrap your values in mysql_real_escape_string($variable). So:
WHERE Something='".mysql_real_escape_string($variable)."'
$animal can be a string which contains '; drop table blah; -- so yes, this is vunerable to SQL injection.
You should look into using prepared statements, where you bind parameters, so that injection cannot occur:
http://us3.php.net/pdo.prepared-statements
It is vulnerable. If I passed: '\'; DROP TABLE ingredient; SELECT \''
as $type, poof, goodbye ingredient table.
If its a private function, kind of. If you're concerned about people with access to the source injecting SQL, there are simpler ways to accomplish their goal. I recommend passing the arguments to mysql_real_escape_string() before use, just to be safe. Like this:
private function _getAllIngredients($animal = null, $type = null) {
$animal = mysql_real_escape_string($animal);
$type = mysql_real_escape_string($type);
...
}
To be extra safe, you might even pass it to htmlentities() with the ENT_QUOTES flag instead - that would also help safeguard against XSS type stuff, unless you're putting the contents of those DB entries into a <script> tag.
But the safest thing you can do is write your own sanitizing function, which would make the best of various techniques, and also allow you to easily protect against new threats which may arise in the future.
Re: UPDATE
The \'' serves to enclose the variable in quotes, so that when it goes through to SQL, nothing is broken by whitespace. IE: Where Something=Cold Turkey would end with an error, where Something='Cold Turkey' would not.
Furthermore, the class extension won't affect how you put together MySQL Statements.
If the the user or any 3rd party has anyway of injecting a value of $animal into your system (which they probably do), then yes it is vunerable to sql injection.
The way to get around this would be to do
private function _getAllIngredients($animal = null, $type = null) {
$ingredients = null;
if($animal != null && $type != null) {
$query = 'SELECT id, name, brief_description, description,
food_type, ingredient_type, image, price,
created_on, updated_on
FROM ingredient
$rows = $this->query($query);
if(count($rows) > 0) {
if($rows['animal'] == $animal && $rows['ingredient_type'] == $type) {
Note: I removed WHERE statements from sql and added if statements to loop