I am trying to be safe from Sql Injection by doing prepared statements + binding on all Database save/update/query actions.
But I got stuck in the moment in which the table name + user_id are variables. code:
// ALL USUAL $dbc MySqli DATABASE CONNECTION - then code statement prepare:
$stmt = $dbc->prepare("UPDATE ".$tablex." SET logo=?, last_mod=? WHERE ".$table_id."=?");
// or: $stmt = $dbc->prepare("UPDATE $tablex SET logo=?, last_mod=? WHERE $table_id=?"); same thing
$stmt->bind_param('ssi', $log_logo, $last_mod, $user_id);
// execute and do some checking
$status_save = $stmt->execute();
if ($status_save === false) {trigger_error($stmt->error, E_USER_ERROR);}
It has worked fine so far as long as $tablex and $table_id were known. Now they can have different names, and using a variable sets things wrong. What am I doing wrong here? Tx.
Related
I want to bind Values to a SQL Statement using a PDO connection to an SQL Server via ODBC.
If I execute the statement without a parameter I get a result but if I want to bind a parameter to that sql I get a "Function sequence error" thrown back.
$fromDate = strval(date('Y-d-m'))." 00:00:00.000";
//works
$sql = "SELECT a.FromDate FROM Absence a WHERE a.FromDate > '".$fromDate."'";
$arrParams = [];
runStatement($sql, $arrParams);
//don't work
$sql = "SELECT a.FromDate FROM Absence a WHERE a.FromDate > :fromdate1";
$arrParams = [':fromdate1' => $fromDate];
runStatement($sql, $arrParams);
//thats how I bind the parameters
function runStatement($sql, $arrParams)
{
$stmt = $this->conn->prepare($sql);
foreach($arrParams as $key => $value)
{
$stmt -> bindValue($key, $value);
}
$stmt -> execute();
return $stmt -> fetchAll(PDO::FETCH_ASSOC);
}
I tried to bind the values with bindParam() but the error remains the same. On Sqlite and Ingres Database the bindValue() works fine for me.
I am using PHP 8.x and SQL Server 2012. Installing the php drivers for SQL doesn't work here.
Edit: I could narrow down the error to the date type. Because in the where clause I want to compare two dates.
When I try other statements without dates as binded params the statement works fine.
So the comparison of the datetype is the error here.
i wrote this code before i was aware of the use of prepared statements and what it does to SQL injections. Now i'm also aware of how messy it is to fetch arrays with prepared statements. So i was wondering if this piece of code is safe to use since it doesn't use any user submitted information to fetch the rows.
What it does is to identify the row in the db table by using a session id, session is ensured by a login_check function etc..:
$username = $_SESSION['username'];
$select = mysqli_query($link, " SELECT product_id, product_title, product_value
FROM product
WHERE user_id='$username'");
while ($row = mysqli_fetch_assoc($select))
{
$product[] = array(
'product_id' => $row['product_id'],
'product_title' => $row['product_title'],
'product_value' => $row['product_value']);
}
Some information regarding this issue would really be appreciated since things were going so well until i got to know of the prepared statements..
Edit
So, i kinda went in another direction and skipped the array part completely for this query. Instead i went with the prepared statement and did something like this..:
$select_stmt = $db->prepare("SELECT etc...)
$select_stmt->bind_param("CODE")
$select_stmt->execute();
And so on..
But the thing is that my bind_result got pretty big (?) with 14 variables. Perhaps this is a stupid question but will that slow down my site compared to the old way with using a single array (if 14 even is considered "big")? This is a common query that hopefully many users will use simultaniously and often. Prepared statements are new for me so..
Thanks sofar for the help people.
You should look into prepared statements. This is one of the many benefits of mysqli. It allows you to insert variables without having to worry about SQL injection. mysqli_real_escape_string will work most times, but prepared statements are the only truly secure method for avoiding attacks.
Example from the manual:
<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$city = "Amersfoort";
/* create a prepared statement */
if ($stmt = $mysqli->prepare("SELECT District FROM City WHERE Name=?")) {
/* bind parameters for markers */
$stmt->bind_param("s", $city);
/* execute query */
$stmt->execute();
/* bind result variables */
$stmt->bind_result($district);
/* fetch value */
$stmt->fetch();
printf("%s is in district %s\n", $city, $district);
/* close statement */
$stmt->close();
}
If the username is e.g. Jean D'arc the string reaching the mysql server would be
SELECT
product_id, product_title, product_value
FROM
product
WHERE
user_id='Jean D'arc'
and that would result in a parse error.
Properly encoding/escaping the parameters within an sql statement is not only necessary for preventing malicious input from users but for every parameter where you can't (with absolute certainty) be sure it doesn't contain characters that may break the statement. In case of (any tiny) doubt encode/escape the parameter, or simply use prepared statements.
This is the code I'm using:
<?php
// Set the MySQL Configuration
$db_host = "";
$db_user = "";
$db_password = "";
$db_name = "";
$db_table = "";
// Start Connection
$db_connect = mysql_connect ($db_host, $db_user, $db_password);
// Select Database
$db_select = mysql_select_db ($db_name, $db_connect);
// Update Values in Database
$query = "UPDATE $db_table SET
age = age + 1,
land = '".$_POST['data3']."'
WHERE name = '".$_POST['data1']."'
";
// Execution MySQL query
$result = mysql_query($query) or die(mysql_error($db_connect));
//Close MySQL connection
mysql_close($db_connect);
//HTTP Response
echo " your age: age";
?>
I want to echo the value of the $age variable, but instead I always get the word "age." For example, the code should echo your age: 5 but instead it outputs your age: age
First, you'll need to run a SELECT query to retrieve the updated value of age. The query should look something like this:
"SELECT age FROM db_table_name WHERE name = ?"
Once you've obtained the result of that query, with say PDO::fetch (see my note below about PDO) and set it to the variable $age, you can output it with an echo statement:
echo "Your age: $age";
Also, please don't use mysql_* functions for new code. They are no longer maintained and the community has begun the deprecation process (see the red box). Instead, you should learn about prepared statements and use either PDO or MySQLi. If you can't decide which, this article will help you. If you care to learn, this is a good PDO tutorial.
The reason I'm not giving you the exact code for this is because it shouldn't be done with the mysql_* functions at all. Creating an SQL query with data directly from $_POST like this is extremely dangerous code to use and an incredibly bad idea all around. Never do this. You open yourself up to numerous SQL injection attacks. Even using mysql_real_escape_string is not enough. You should be using prepared statements.
UPDATE: Here is a simple example that's close to what you're asking, but using PDO and prepared statements. This is by no means a comprehensive example, since there are several ways to alter it that will still work (e.g. prepared statements allow you to execute multiple statements on the server in one statement), and I don't have a working server at the moment to test to make sure it's exactly what you need, but I hope it gets the point of across.
<?php
// Create the database connection
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 'username', 'password');
// Set PDO/MySQL to use real prepared statements instead of emulating them
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// The UPDATE query we're going to use
$update_query = "UPDATE `db_table_name` SET age = age + 1, land = :land WHERE name = :name";
// Prepare the query
$stmt = $db->prepare($update_query);
// Bind variables to the named parameters in the query with their values from $_POST
$land = $_POST['data3'];
$name = $_POST['data1']
$stmt->bindParam(':land', $land);
$stmt->bindParam(':name', $name);
// Execute the statement on the server
$stmt->execute();
// The SELECT query we're going to use
$select_query = "SELECT age FROM `db_table_name` WHERE name = :name";
// Again, prepare the query
$stmt_select = $db->prepare($select_query);
// Bind the paramters (in this case only one) to the new statement
// $name is already set from before, so there is no need to set it again
$stmt_select->bindParam(":name", $name);
$stmt_select->execute();
/*
* With no arguments, PDO::fetchColumn() returns the first column
* in the current row of the result set. Otherwise, fetchColumn()
* takes a 0-indexed number of the column you wish to retrieve
* from the row.
*/
$age = $stmt_select->fetchColumn();
echo("Your age: $age");
?>
All of this information came directly from the PHP documentation on prepared statements and PDO::fetchColumn().
I need to optimize a script for high performance so the question is how can I add MySQL multi-query to this code?
foreach($multiContent as $htmlContent) {
$email = urldecode($email['1']);
$user = $user['1'];
//store in db
$db->query("UPDATE eba_users
SET mail = '$email'
WHERE username = '$user'");
//echo "email is $email and user is $user\n";
}
//close if ($array_counter % 200
unset($array_counter);
unset($data);
}
If you're using mysqli or PDO already, you should be using prepared statements for your queries since they are supported. This will also have a slight increase in performance since the entire query doesn't need to be sent again to the DB server. However the biggest advantage is the increased security that prepared statements provide.
Other than this, try adding an index on username to speed this query up if you haven't already.
Edit:
If you want to do it all in one query, as you seem to suggest, you could also use ON DUPLICATE KEY UPDATE as mentioned as an answer to this question:
INSERT INTO eba_users (`username`, `mail`)
VALUES
('username1','$email1'),
('username2','$email2'),
('username3','$email3'),
('username4','$email4'),
....
('usernameN','$emailN'),
ON DUPLICATE KEY UPDATE `mail`=VALUES(mail);
However this may not be as fast as using prepared statements with a regular UPDATE.
Edit2: As requested, here is probably a close approximation of what you should be doing to bind the parameters in mysqli:
if ($stmt = $mysqli->prepare("UPDATE eba_users SET mail= ? WHERE username= ?")) {
/* loop through array of users */
foreach ($array as $username => $newemail) {
/* bind parameters for markers */
$stmt->bind_param("ss", $newemail, $username);
/* execute query */
$stmt->execute();
}
}
Of course this doesn't provide any sort of error messages in case this fails. For that, you can look into mysqli::error
Do I need to use mysql_real_escape_string() on my input (such as $_POST and $_GET) when I use the PDO library?
How do I properly escape user input with PDO?
If you use PDO you can parametize your queries, removing the need to escape any included variables.
See here for a great introductory tutorial for PDO.
Using PDO you can seperate the SQL and passed parameters using prepared statements, this removes the need to escape strings, as because the two are held seperately then combined at execution, the parameters are automatically handled as stings, from the above source:
// where $dbh is your PDO connection
$stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
/*** bind the paramaters ***/
$stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
$stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
/*** execute the prepared statement ***/
$stmt->execute();
Note: sanitization occurs during variable binding ($stmt->bindParam)
Other resources:
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
http://www.phpeveryday.com/articles/PDO-Prepared-Statement-P550.html
http://php.net/manual/en/pdo.prepared-statements.php
The important point when using PDO is:
PDO will only sanitize it for SQL, not for your application.
So yes, for writes, such as INSERT or UPDATE, it’s especially critical to still filter your data first and sanitize it for other things (removal of HTML tags, JavaScript, etc).
<?php
$pdo = new PDO(...);
$stmt = $pdo->prepare('UPDATE users SET name = :name WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filter your data first
$name = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_STRING); // <-- filter your data first
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- Automatically sanitized for SQL by PDO
$stmt->bindParam(':name', $name, PDO::PARAM_STR); // <-- Automatically sanitized for SQL by PDO
$stmt->execute();
Without sanitizing the user input, a hacker could have saved some javascript into your database and then, when output it into your site you would have been exposed to a threat!
http://www.phptherightway.com/#pdo_extension