PHP MySQL prepared statement: procedural Insert / update example [duplicate] - php

This question already has answers here:
How to use mysqli prepared statements?
(3 answers)
Closed 11 months ago.
I struggle to understand prepared statements in PHP to allow users to edit a MySQL-database.
The user input is UTF-8, typical examples are the name in Arabic, Chinese, ... It also generates a problem when using Geo-location as 47°23'15"N, 4°12°27"E, as is visible i.e. in Wikipedia.
Best lead I found to my problem to insert, insert ignore, on duplicate key ... to update datasets in a database from user-input using prepared statements. An interesting lead is in PHP Insert Prepared Statement, but that's "PDO", which I happen to lack any experience with.
So far I tried to sanitize, now it seems to me that it might be far easier and safer to use prepared statements. But. I'm bloody amateur. And use procedural statements. And never tried a prepared statement before. And the input expects better understanding.
So I would like to take something like
<?php
$name = $_POST['name'];
$user = filter_var($_POST['user'], FILTER_SANITIZE_EMAIL);
$descr = $_POST['utf8text'];
$geo = $_POST['geo']; // See [1] below.
?>
[1] Which is i.e. 47°23'15"N, 4°12'27"E and I am not sure how to properly escape it? filter_var($_POST['geo'],FILTER_SANITIZE_ADD_SLASHES) returns 47°23\'15\"N, 4°12\'27\"E?
Then to enter this into an SQL database like traditional
<?php
$link = mysqli_connect('localhost', 'user', 'pass','database');
mysqli_set_charset($link,'utf8');
$insertsql = "INSERT INTO `database` (`name`,`user`,`descr`, geo)
VALUES ('". $name . "', '" . $user . "', '" . $descr . "', " . $geo . "')
ON DUPLICATE KEY UPDATE `descr`='" . $descr . "', '" . $geo . "';
mysqli_query($link,$insertsql);
?>
Okayokay, using added code to make sure the database connection works, the query is processed properly and failure handling. But I want to simplify.
My question would be, how I would prepare such group of values for a prepared statement. And I believe to understand I must replace mysqli_query with mysqli_prepare and I need a count of the fields addressed and use "ssss" but don't find any explanation in any of the manuals what the s's (multiple "s") do.
What I tried didn't work (yet) and I need a working example to understand what I have to do. I tried to adjust the example in above linked article without PDO to no success.
And I am worried if/how the prepared data is transferred in strings when using ON DUPLICATE KEY, as I don't find any explanation there either (PEBKAS?).
Any help appreciated!

If I understood correctly, you want to make a mysql prepared query?
(I apologize if I misunderstood the problem)
But here my solution and explanation. First :
$stmt = mysqli_prepare($link, "INSERT INTO `database` (`name`,`user`,`descr`, geo) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE descr= ?, ?;");
Here $link is your mysqli_connect
You set ? where you want to put your future variables
mysqli_stmt_bind_param($stmt, 'ssssss', $name, $user, $descr, $geo, $descr, $geo);
All ? need to be defenied what types of variable it's gonna be, here it's gonna be a string so we set S (You can find the others possibilities for the second parameter here)
Then all ? will be bound to your variables in the order in which you want it to appear instead of ?
mysqli_stmt_execute($stmt)
And finally you execute the query

Related

mysqli insert error incorrect syntax [duplicate]

This question already has answers here:
How to include a PHP variable inside a MySQL statement
(5 answers)
Closed 3 years ago.
I know a lot of people have the same error occasionally however I have looked at all previous answers and my code and i have tried col with and without backticks
Here is my current code
I also have tried with $var as well as just $var but same
if(!empty($_POST['email'])){
$date = date('dmY'); #Todays Date
$ip = str_replace('.','',$_SERVER['REMOTE_ADDR']); #Visitor IP
$verify = md5($date.$ip); #MD5 ENCRYPT THE 2 VALUES
$fname = $_POST['fname'];
$lname = $_POST['lname'];
$email = $_POST['email'];
$password = md5($_POST['password']);
$link = mysqli_connect($dbh,$dbu, $dbp, $dbn);
$query = mysqli_query($link, "INSERT INTO `users` (`email`,`fname`,`lname`,`verify`,`password`,`joined`)
VALUES($email,$fname,$lname,$verify,$password,$date)");
if($query){
echo "inserted";
}
else {
echo mysqli_error($link);
}
There are other columns in the table however its only the above columns I want to add data for the rest can use default values initially
I've been looking at this code for so long now I just cant spot my problem, I know its something silly
The most mistake-proof way to add a variable into an SQL query is to add it through a prepared statement.
So, for every query you run, if at least one variable is going to be used, you have to substitute it with a placeholder, then prepare your query, and then execute it, passing variables separately.
First of all, you have to alter your query, adding placeholders in place of variables. Your query will become:
$sql = "INSERT INTO users (fname, lname) VALUES (?, ?)";
Then, you will have to prepare it, bind variables, and execute:
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, "ss", $fname, $lname);
mysqli_stmt_execute($stmt);
As you can see, it's just three simple commands:
prepare() where you send a query with placeholders
bind_param where you send a string with types ("s" is for string and you can use it for any type actually) and than actual variables.
and execute()
This way, you can always be sure that not a single SQL syntax error can be caused by the data you added to the query! As a bonus, this code is bullet-proof against SQL injection too!
It is very important to understand that simply adding quotes around a variable is not enough and will eventually lead to innumerable problems, from syntax errors to SQL injections. On the other hand, due to the very nature of prepared statements, it's a bullet-proof solution that makes it impossible to introduce any problem through a data variable.

Column count doesn't match value count at row 1 (columns and values are equal)

I'm getting the error: Column count doesn't match value count at row 1
I think, normally this error occurs if the count of the columns and the values aren't equal, but in my code they are...(3).
This is my php code:
$tempsongtitel = $_POST['songtitle'];
$tempinterpret = $_POST['interpret'];
$templink = $_POST['link'];
$query = mysql_query("insert into tMusic (Songtitel, Interpret, Link) values ('$tempsongtitel, $tempinterpret, $templink')") or die(mysql_error());
You missed some quotes. Should be:
$query = mysql_query("insert into tMusic (Songtitel, Interpret, Link) values ('$tempsongtitel', '$tempinterpret', '$templink')") or die(mysql_error());
Otherwise, you were trying to insert all three POST values into the first field.
Moreover, the mysql_ extension has been deprecated and is on the way out and is highly discouraged, especially if you are creating new software.
AND I'll presume you are first sanitizing your data? You're not really taking user input and placing it directly into the database, are you? Even if you don't do any data validation, you should escape your data in the query... easiest and most foolproof way to do that is by using parameterized queries.
The root cause is that your values are all in one set of quotes instead of quoted individually. I think this is a pretty common error, and in my experience it is an easy mistake to make, but not immediately obvious when scanning over your code. You can fix it like this (quick fix, still using deprecated mysql, but with post values escaped):
$tempsongtitel = mysql_escape_string($_POST['songtitle']);
$tempinterpret = mysql_escape_string($_POST['interpret']);
$templink = mysql_escape_string($_POST['link']);
$query = mysql_query("insert into tMusic (Songtitel, Interpret, Link)
values ('$tempsongtitel', '$tempinterpret', '$templink')") or die(mysql_error());
If you can, it would be much better to update your code to use PDO. You could use a prepared statement like this:
$stmt = $pdo->prepare("INSERT INTO tMusic (Songtitel, Interpret, Link) VALUES (?, ?, ?)");
$stmt->bindValue(1, $tempsongtitel);
$stmt->bindValue(2, $tempinterpret);
$stmt->bindValue(3, $templink);
$stmt->execute();
Among the many benefits of using this database extension rather than the old mysql functions it should not be possible to make an error like this in your code. In the prepared statement, there are no quotes around the parameter markers, so if you have VALUES ('?, ?, ?'), or even VALUES ('?', '?', '?') You would get bind errors when trying to bind the values, and the problem would become apparent pretty quickly.
I've found that, even though it's not 100% necessary and it's more time consuming, properly quoting and backticking EVERYTHING helps prevent this from happening.
$myQuery = "INSERT INTO `tMusic` (
`Songtitel`,
`Interpret`,
`Link`
) VALUES (
'$tempsongtitel',
'$tempinterpret',
'$templink'
);";
$runQuery = mysqi_query($DBi, $myQuery) or die(mysqli_error($DBi));
The formatting you use is up to you but this helps me make sure I have a one to one relationship and that I've quoted everything.
Of course that's using mysqli_* in place of the deprecated mysql_* functions AND that's assuming you've set $tempsongtitel, $tempinterpret and $templink properly.

Prevent SQL Injection by input type text [duplicate]

This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 8 years ago.
I need some help and very very fast because my database was injected. I need at least a script that won't allow users to use :[Spaces, Sybols like ('*','=','/','.') and a list of words ('SELECT','FROM','WHERE')] in the text fields of my register form.
I heared something about mysql_real_escape_string(). What is this command doing? And don't post links to PHP: mysql_real_escape_string() Manual because I already read that.
There'a a right and a wrong way to approach this. The (usually) wrong way is to try and set up an input sanitation method (like a script) and hope that nothing gets through. It usually doesn't work.
What I recommend you to do is rewrite your PHP SQL queries to use MySQLi prepared statements. These are queries that are first converted from the common SQL syntax ("SELECT... WHERE...") to a statement your engine can work with, and only then are the fields replaced with your input, thus preventing SQL injection.
For example, the (very) susceptible SQL syntax:
"SELECT * FROM users_passwords WHERE user='" + user + "' AND pass='" + password + "'"
Can be converted to the following prepared statement:
"SELECT * FROM users_passwords WHERE user=? AND password=?"
And then, using the command bind_param(), you can safely replace the ? placeholders with your parameters after the statement is prepared. While the original SQL query allows you to use some basic injection techniques (like writing ' OR true OR '), prepared statements will not allow this.
Here's a working example:
// Create a new MySQLi connection object
$db = new mysqli('localhost','db_username','db_password','db_name');
// Create a new prepared statement
$stmt = $db->prepare('SELECT * FROM users_passwords WHERE user=? AND pass=?');
// Bind the parameters, in order, to the statement (s stands for string)
$stmt->bind_param('ss', username, password);
// Self-explanatory
$stmt->execute();
If you are in PHP then why don't you do it in your PHP script. sanitize all your user provided input in GET and POST and then move it forward to DB calls. That is the right way to do it.
I would strongly avoid constructing SQL query strings from any input even if you sanitize it.
The good way for security purposes and performance is to use functions to set the parameters:
for example:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
see http://php.net/manual/en/pdo.prepared-statements.php

Prepared statements and Escaping

I've read many times over - and just want to clarify (I think I'm confused)
I switched to mysqli today, and started using prepared statements.
Example of my prepared statement
function read($table, $var) {
if($stmt = mysqli_prepare($link, "SELECT * FROM ? WHERE `uid`=?")) {
mysqli_stmt_bind_param($stmt, "si", $table, $var);
mysqli_stmt_execute($stmt);
return mysqli_fetch_assoc($stmt);
} else {
echo '<script type="text/javascript>">alert("Something went wrong");</script>';
}
}
$info = read("users", $_SESSION['uid']);
$char = read("characters", $_SESSION['uid']);
Do i still need to escape anything? I know, i know, i've read it everywhere that you dont need to escape when using prepared statements, but then there are questions like this and this that make me worried.
the only problem with you query is that you cannot pass the tableName as a paramater. Only values can be parameterized. So the other way is to concatenate the tableName along with your query.
"SELECT * FROM `" . $tableNameHere . "` WHERE `uid`=?"
Well, first of all this code just won't work.
So, there are 2 questions actually
Do I need to do any additional escaping on the bound parameters
No.
How to safely insert an identifier into query?
It depends. As long as you have your table name hardcoded in your code - it's ok to insert it as is.
But if it's coming from the untrusted source, you have to filter it out, using whitelisting.
I've explained it in my other answer https://stackoverflow.com/a/8255054/285587
As for the questions you linked to, the second one is irrelevant and first one just makes very little sense. LIKE is supposed to return many rows, so, one have to either use no LIKE at all or worry not about it (in terms of safety). Though in terms of returning correct result you may want to escape characters that have special meaning in LIKE, but I wouldn't use LIKE for the search purposes at all.

How should I write PHP $_POST vars in a mysql_query function?

In accessing my database, I have the user fill out a form, and in the target page, the posted values are used in the resulting MySQL query.
$query = mysql_query("SELECT pass FROM database WHERE user='$_POST[user]'");
However, for some reason or another, MySQL doesn't like my using a $_POST variable in the command, and it only works if I define (for example) $user = $_POST['user'];, and then put $user directly in the SQL command.
On the other hand, I can use $_POST values in INSERT statements where specific column names are not required:
$query = mysql_query("INSERT INTO database VALUES ('foo', 'bar', '$_POST[user]'");
If I try an INSERT statement where attributes are defined (e.g. user='foo'), then the same problem appears.
What am I doing wrong in my SQL query that causes the command to error out when run, but works with the specific method of formatting an INSERT command?
Hopefully, it's not "tough luck, looks like you have to assign all of your posted values". Heh.
First of, watch out for SQL Injections!
Now, to answer your question try doing this instead:
$query = mysql_query("SELECT `pass` FROM `database` WHERE `user` LIKE '" . mysql_escape_string($_POST['user']) . "';");
You were doing a couple of things wrong:
using the = operator instead of LIKE operator
not enclosing the value in the SQL query with '
not enclosing the user index in the $_POST array with '
PS: You should use mysql_real_escape_string() instead of mysql_escape_string()!
You're simply inserting a variable into a string, so it shouldn't matter which command you're putting it into.
There are a few issues to point out.
One, you might want to use the {} format for array variables. You don't use quotes around the arrray key names in this format.
$query = mysql_query("SELECT pass FROM database WHERE user='{$_POST[user]}'")
Two, you'd never want to make a query like that because you are open to sql injection holes. Consider, what if $_POST['user'] was "cow';drop table database;--"?
You must either run mysql_real_escape_string on the POST input before putting it into your query, or check out using PHP PDO with prepared statements.
One way to do format your string which provides a bit of structure is to use sprintf.
$query=mysql_query(sprintf("SELECT pass FROM database WHERE user='%s'",mysql_real_escape_string($_POST['user'])));
Use PDO - it provides much better API to communicate with DB.
If you're using mysql_*() functions always remember to filter (mysql_real_escape_string()) any data that comes from untrusted source (like user)
Pay more attention to how your code looks like. Just compare the following listings:
$query = mysql_query("INSERT INTO database VALUES ('foo', 'bar', " . mysql_real_escape_string($_POST['user']) . ", " . mysql_real_escape_string($_POST['user']) . ", " . mysql_real_escape_string($_POST['user']) . ", " . mysql_real_escape_string($_POST['user']) . ")");
$query = sprinf('INSERT INTO database VALUES ("foo", "bar", "%s", "%s", "%s")',
mysql_real_escape(...), ...);
Do I have to explain which one is better to read, modify or understand?
Why not check and see what mysql_error() has to say about it? If your query is invalid, mysql_error() will return a nice blob of text telling you exactly what went wrong.
As for MySQL not liking the POST var if you insert it directly for some runs, but not others, then you should make sure you're using consistent data and setups for each test. If some test are done using a GET, then your POST vars will be empty. If you're using different user names for each test, then see if what's consistent between the ones that fail.
And as mentioned above, read up about SQL injection and how your query is just begging to be subverted by a malicious user.
Try
$query = mysql_query("SELECT pass FROM database WHERE user=" . mysql_real_escape_string($_POST['user']));
and
$query = mysql_query("INSERT INTO database VALUES ('foo', 'bar', " . mysql_real_escape_string($_POST['user']) . ")");
Its always a good idea to sanitize anything received through $_GET or $_POST

Categories