A few keep telling me that my code for updating data in my mysqli query is extremely insecure. Actually, several people on this site. So I would like to know what they say would secure my code below so it is secure when updating my database. I would like to know how the would secure my mysqli query.
Okay, in my code for my database entries, this is what I do. Let me start by saying that I always send via POST method to avoid browser url complications.
When I get the POST data, this is my code.
$ID = 1;
$DATA = htmlentities(addslashes($_POST['data']));
$FIELD = "lifename";
$DBQUERY = "UPDATE `lifetable` SET `$FIELD` = '$DATA' WHERE `id` = $ID";
$DBRESULT = $MYSQLI->query($DBQUERY);
I am currently using this on my local site.
How is this unsafe if I have escaped all quotes, all slashes, all ampersands (from javascript through ajax) and all semi colons? How is this vunerable?
So can you tell me what I should change when adding information to my database.
Thanks
PS ... I am using mysqli and will continue to use it. Thanks
A few suggested that I change from mysqli to pdo, but I am not willing to completely 100% change how I access data from my databases. Someone posted another link before about prepare and bind_param and this is what I am going to use. So thank you.
This is now my code, and binding params is supposed to make it so that each param is only for the one part of my query and can not be for anything else, nothing else at all.
$DBQUERY = "UPDATE `lifetable` SET `lifename` = ? WHERE `id` = ?"; // EACH ? IS A PART OF bind_param BELOW IN ORDER AFTER TYPE.
$STMT = $MYSQLI->prepare($DBQUERY);
$STMT->bind_param('si', $DATA, $ID); // THIS MAKES SURE THAT THE VARIABLES ARE ONLY USED FOR THERE PLACE HERE AND NOTHING ELSE. ? in order.
$DATA = htmlentities($_POST['data']); // I STILL USE MY CODE HERE TO REMOVED ANY OTEHR CHARACTERS, JUST INCASE. AND BEFORE IT GETS HERE, IT USES encodeURIComponent TO OUTPUT FROM AJAX.
$ID = $COLUMN[1];
$STMT->execute();
$STMT->close();
My code worked before and it works now, just more secure, or so I am told.
Use PDO Class like:
$db = new PDO('mysql:host=localhost;dbname=<SOMEDB>', '<USERNAME>', 'PASSWORD');
$query = $db->prepare('UPDATE `lifetable` SET :FIELD = :DATA WHERE `id` = :ID');
$query->execute(array(
':FIELD' => $field,
':DATA' => $data,
':ID' => $id
));
$query->commit();
For more info Are there good tutorials on how to use PDO?
i think your security lies in the SQL injection, and the best way i know to make the query secure is using mysql_real_escape_string on the var. Here an example taken from php.net
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
$city = $mysqli->real_escape_string($city);
$mysqli->query("INSERT into myCity (Name) VALUES ('$city')")
you can apply the same procedure to your query
$ID = 1;
$DATA = $MYSQLI->real_escape_string($_POST['data']));
$FIELD = "lifename";
$DBQUERY = "UPDATE `lifetable` SET `$FIELD` = '$DATA' WHERE `id` = $ID";
$DBRESULT = $MYSQLI->query($DBQUERY);
I edited the above because I forgot the quotes for lifename in my question. They should be there as they are in my original code.
now tour query should be secure :D
here the reference to php.net documentation :
http://cn2.php.net/manual/en/mysqli.real-escape-string.php
Related
I've just created a simple API for a CAD/MDT I'm working on, I've managed to get it to show the correct information when I do /citations/userid/1. This will then display all the correct values from the SQL database however, if I do /citations/issued_by/kevingorman1000 it will just throw an error. I can't tell what the error is as I'm using Slim php and can't seem to get the errors to display.
Any ideas why it isn't working ? I've added my code below..
$app->get('/citation/issuedby/{issued_by}', function(Request $request, Response $response){
$issued_by = $request->getAttribute('issued_by');
$sql = "SELECT * FROM ncic_citations WHERE issuedby = $issuedby";
try{
// Get DB Object
$db = new db();
// Call Connection to DB
$db = $db->connect();
$stmt = $db->query($sql);
$issby = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
echo json_encode($issby);
} catch(PDOExecption $e) {
echo '{"error"} : {"text": '.$e->getMessage().'}';
}});
Any ideas why this is the case? Does it only allow getting via number or do I need too do something else? First time using this and kinda new to PHP as well.
Thanks for any help.
Your problem is called SQL injection. You can solve it by using prepared statements. Never escape the values with quotes or anything else, as others might have suggested.
$sql = "SELECT * FROM ncic_citations WHERE issuedby = ? ";
$stmt = $db->prepare($sql);
$stmt->execute([$issuedby]);
$issby = $stmt->fetchAll(PDO::FETCH_OBJ);
For a good tutorial on PDO and prepared statements I recommend: https://phpdelusions.net/pdo
It's because SQL error (missing quotes around string).
You try to send query
$sql = "SELECT * FROM ncic_citations WHERE issuedby = kevingorman1000";
Correct query has to be
$sql = "SELECT * FROM ncic_citations WHERE issuedby = 'kevingorman1000'";
I have found the PDO::FETCH_CLASS very useful. My classes map to tables. I just do a
$query = $pdo->query("SELECT * FROM fixedTime WHERE
transmissionProgramID = '$transmissionProgramID'");
$query->setFetchMode(PDO::FETCH_CLASS, 'FixedTime');
and voila.
I would like to be able to do the reverse: ie instantiate an object load up the values to UPDATE or INSERT and once again voila.
Have looked but cannot see if this is available.
Well, yes. To some extent you can use something similar for insert or update.
But to achieve that, you have to learn how to use PDO properly. So, first we have to fix your select code:
$sql = "SELECT * FROM fixedTime WHERE transmissionProgramID = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$transmissionProgramID]);
$stmt->setFetchMode(PDO::FETCH_CLASS, 'FixedTime');
$ftime = $stmt->fetch();
See - we are using prepared statements here, that you should be always using anyway. And at the same time that's the key for the [semi-]automation we can use with updates. Because with prepared statements you can use the object itself to provide values for the prepared query.
So, as long as you have object properties reflect table fields you can use a code like this:
$user = new stdClass();
$user->name = "foo";
$user->pass = "bar";
$sql = "INSERT INTO users VALUES (NULL, :name, :pass)";
$pdo->prepare($sql)->execute((array)$user);
But for the real automation you have to consider using an ORM, which is doing exactly what you're looking for. You can take a look at Eloquent for example. So, the code would be as simple and straightforward as
$ftime = new fixedTime;
$ftime->value = time();
$ftime->save();
I'mm currently going through the process of changing my code and have hit a bit of a wall so thought id ask and see if anyone can help me with the new code...
firstly i have quite a few mysql_result() dotted around my code and would like to know how to write the code for the new process.. here is an example of one of my functions that has code that needs changing, I've done some its the mysql_result() part that I need to know how to rewrite..
function user_exists($db,$username) {
$username = sanitize($db,$username);
$sql = $db->query("SELECT COUNT(`user_id`) FROM `users` WHERE `username` = '$username'");
return(mysql_result($sql, 0) == 1) ? true : false;
}
Also while I'm here I may as well ask.. I also have this function to, well sanitize code but i keep reading that mysql_real_escape_string is not so safe. I've read that i should be using prepared statements, I'm just not to sure how to implement it into my code..here is the sanitize function that is basically just mysql_real_escape_string()..
function sanitize($db, $data) {
return mysqli_real_escape_string($db, $data);
}
so how would i make this correct? cause from what im reading its just putting prepare before a query like..
$sql = $db->prepare("SELECT COUNT(`user_id`) FROM `users` WHERE `username` = '$username'");
so my question is, can anyone help me change these codes to correct up-to date versions?
What you want to use is the PDO Class and bind values (http://www.php.net/manual/en/class.pdo.php).
So something like the following should work:
$stmt = $db->prepare("SELECT 1 FROM users WHERE username = ?");
$stmt->execute([$username]);
return $stmt->fetchColumn();
What would be the best way to protect this query from sql injection?
This example is just an example, I've read a few articles on internet but can't get my head around parametrised queries. Any links to useful articles will get a vote up but I think seeing this example would help me best.
$id = $_GET["id"];
$connection = odbc_connect("Driver={SQL Server};Server=SERVERNAME;Database=DATABASE-NAME;", "USERNAME", "PASSWORD");
$query = "SELECT id firstname secondname from user where id = $id";
$result = odbc_exec($connection, $query);
while ($data[] = odbc_fetch_array($result));
odbc_close($connection);
Thanks,
EDIT: I didn't make it obvious but I'm using SQL Server not mysql.
This is just an example, it won't always be a number I'm searching on.
It would be nice if the answer used parametrised queries as many people suggest this and it would be the same for all query's instead of different types of validation for different types of user input.
I think PDO objects are the best.
In a nutshell, here is how you use them.
$databaseConnection = new PDO('mysql:host='. $host .';dbname=' . $databaseName, $username, $password);
$sqlCommand = 'SELECT foo FROM bar WHERE baz=:baz_value;';
$parameters = array(
':baz_value' => 'some value'
);
$preparedStatement = $databaseConnection->prepare($sqlCommand);
$preparedStatement->execute($parameters);
while($row = $preparedStatement->fetch(PDO::FETCH_ASSOC))
{
echo $row['foo'] . '<br />';
}
The values you would enter for the SELECT criteria are replaced with parameters (like :field_value) that begin with a colon. The paramters are then assigned values in an array which are passed separately.
This is a much better way of handling SQL queries in my opinion.
The parameters are sent to the database separately from the query and protects from SQL injection.
Use prepared statements. First build a statement with the odbc_prepare() function, then pass the parameters to it and execute it using odbc_execute().
This is much more secure and easier than escaping the string yourself.
Lewis Bassett's advice about PDO is good, but it is possible to use prepared statements with ODBC without having to switch to PDO.
Example code, untested!
try {
$dbh = new PDO(CONNECTION_DETAILS_GO_HERE);
$query = 'SELECT id firstname secondname from user where id = :id';
$stmt = $dbh->prepare($query);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$result = $stmt->execute();
$data = $stmt->fetchAll();
} catch (PDOException $e)
echo 'Problem: ', $e->getMessage;
}
Note: $e->getMessage(); may expose things you don't want exposed so you'll probably want to do something different on that line when your code goes live. It's useful for debugging though.
Edit: Not sure if you wanted a PDO or ODBC example but it's basically the same for both.
Edit: If you're downvoting me please leave a comment and tell me why.
To begin with, be careful with the variables you use in your queries, specially those that come from external sources such as $_GET, $_POST, $_COOKIE and $_FILES. In order to use variables inside your queries you should:
Cast numeric data to integer or float (whichever is appropriate)
Use appropriate escaping to escape other data
A simple example for mysql databases:
$id = $_GET["id"]; // contains: OR 1 = 1
$name = $_GET["name"]; // contains: ' OR '' ='
$query = "SELECT * FROM table WHERE id = " . intval($id) . " AND name = '" . mysql_real_escape_string($name) . "'";
// SELECT * FROM table WHERE id = 0 AND name = '\' OR \'\' =\''
For other database, the escaping practice varies. But generally you're supposed to escape the ' character with '', so:
$id = $_GET["id"]; // contains: OR 1 = 1
$name = $_GET["name"]; // contains: ' OR '' ='
$query = "SELECT * FROM table WHERE id = " . intval($id) . " AND name = '" . str_replace("'", "''", $name) . "'";
// SELECT * FROM table WHERE id = 0 AND name = ''' OR '''' ='''
Having said that, perhaps you might want to switch to PDO. It allows you to use prepared statements, the PDO driver does all the escaping.
The mysql variant came with a method called mysql_real_escape_string, which was appropriate for the version of SQL being targeted. The best thing you can do is write a method to escape the Id. It's important that your escape method is appropriate for the target database. You can also do basic type checking like is_numeric for numeric inputs will reject SQL string injections immediately.
See How to escape strings in SQL Server using PHP?
and follow some of the related links for explicit examples
I'm playing around with MySQLi at the moment, trying to figure out how it all works. In my current projects I always like to echo out a query string while coding, just to make sure that everything is correct, and to quickly debug my code. But... how can I do this with a prepared MySQLi statement?
Example:
$id = 1;
$baz = 'something';
if ($stmt = $mysqli->prepare("SELECT foo FROM bar WHERE id=? AND baz=?")) {
$stmt->bind_param('is',$id,$baz);
// how to preview this prepared query before acutally executing it?
// $stmt->execute();
}
I've been going through this list (http://www.php.net/mysqli) but without any luck.
EDIT
Well, if it's not possible from within MySQLi, maybe I'll stick with something like this:
function preparedQuery($sql,$params) {
for ($i=0; $i<count($params); $i++) {
$sql = preg_replace('/\?/',$params[$i],$sql,1);
}
return $sql;
}
$id = 1;
$baz = 'something';
$sql = "SELECT foo FROM bar WHERE id=? AND baz=?";
echo preparedQuery($sql,array($id,$baz));
// outputs: SELECT foo FROM bar WHERE id=1 AND baz=something
Far from perfect obviously, since it's still pretty redundant — something I wanted to prevent — and it also doesn't give me an idea as to what's being done with the data by MySQLi. But I guess this way I can quickly see if all the data is present and in the right place, and it'll save me some time compared to fitting in the variables manually into the query — that can be a pain with many vars.
I don't think you can - at least not in the way that you were hoping for. You would either have to build the query string yourself and execute it (ie without using a statement), or seek out or create a wrapper that supports that functionality. The one I use is Zend_Db, and this is how I would do it:
$id = 5;
$baz = 'shazam';
$select = $db->select()->from('bar','foo')
->where('id = ?', $id)
->where('baz = ?', $baz); // Zend_Db_Select will properly quote stuff for you
print_r($select->__toString()); // prints SELECT `bar`.`foo` FROM `bar` WHERE (id = 5) AND (baz = 'shazam')
I have struggled with this one in the past. So to get round it I wrote a little function to build the SQL for me based on the SQL, flags and variables.
//////////// Test Data //////////////
$_GET['filmID'] = 232;
$_GET['filmName'] = "Titanic";
$_GET['filmPrice'] = 10.99;
//////////// Helper Function //////////////
function debug_bind_param(){
$numargs = func_num_args();
$numVars = $numargs - 2;
$arg2 = func_get_arg(1);
$flagsAr = str_split($arg2);
$showAr = array();
for($i=0;$i<$numargs;$i++){
switch($flagsAr[$i]){
case 's' : $showAr[] = "'".func_get_arg($i+2)."'";
break;
case 'i' : $showAr[] = func_get_arg($i+2);
break;
case 'd' : $showAr[] = func_get_arg($i+2);
break;
case 'b' : $showAr[] = "'".func_get_arg($i+2)."'";
break;
}
}
$query = func_get_arg(0);
$querysAr = str_split($query);
$lengthQuery = count($querysAr);
$j = 0;
$display = "";
for($i=0;$i<$lengthQuery;$i++){
if($querysAr[$i] === '?'){
$display .= $showAr[$j];
$j++;
}else{
$display .= $querysAr[$i];
}
}
if($j != $numVars){
$display = "Mismatch on Variables to Placeholders (?)";
}
return $display;
}
//////////// Test and echo return //////////////
echo debug_bind_param("SELECT filmName FROM movies WHERE filmID = ? AND filmName = ? AND price = ?", "isd", $_GET['filmID'], $_GET['filmName'], $_GET['filmPrice']);
I have also build a little online tool to help.
Mysqli Prepare Statement Checker
I recently updated this project to include composer integration, unit testing and to better handle accepting arguments by reference (this requires updating to php 5.6).
In response to a request I received on a project I created to address this same issue using PDO, I created an extension to mysqli on github that seems like it addresses your issue:
https://github.com/noahheck/E_mysqli
This is a set of classes that extend the native mysqli and mysqli_stmt classes to allow you to view an example of the query to be executed on the db server by interpolating the bound parameters into the prepared query then giving you access to resultant query string as a new property on the stmt object:
$mysqli = new E_mysqli($dbHost, $dbUser, $dbPass, $dbName);
$query = "UPDATE registration SET name = ?, email = ? WHERE entryId = ?";
$stmt = $mysqli->prepare($query);
$stmt->bindParam("ssi", $_POST['name'], $_POST['email'], $_POST['entryId']);
$stmt->execute();
echo $stmt->fullQuery;
Will result in:
UPDATE registration SET name = 'Sue O\'reilly', email = 'sue.o#example.com' WHERE entryId = 5569
Note that the values in the fullQuery are escaped appropriately taking into account the character set on the db server, which should make this functionality suitable for e.g. log files, backups, etc.
There are a few caveats to using this, outlined in the ReadMe on the github project, but, especially for development, learning and testing, this should provide some helpful functionality.
As I've outlined in the github project, I don't have any practical experience using the mysqli extension, and this project was created at the request of users of it's sister project, so any feedback that can be provided from devs using this in production would be greatly appreciated.
Disclaimer - As I said, I made this extension.
Just set it to die and output the last executed query. The Error handling should give you meaningful information which you can use to fix up your query.
You can turn on log queries on mysql server.
Just execute command:
sql> SHOW VARIABLES LIKE "general_log%";
sql> SET GLOBAL general_log = 'ON';
And watch queries in the log file.
After testing turn log off:
sql> SET GLOBAL general_log = 'OFF';
I was able to use var_dump() to at least get a little more info on the mysqli_stmt:
$postmeta_sql = "INSERT INTO $db_new.wp_postmeta (post_id, meta_key, meta_value) VALUES (?, ?, ?)";
$stmt = $new_conn->prepare($postmeta_sql);
$stmt->bind_param("sss", $post_id, $meta_key, $meta_value);
echo var_dump($stmt);
$stmt->execute();
$stmt->close();