I'm trying to ensure that I avoid any SQL injections so I'm curious how WordPress handles these types of situations.
I would like to use the WordPress wrapper to insert new values into the database. Say I have the following snippet:
<?PHP
$var = $_POST['var'];
$qry = $wpdb->insert(
'my_table',
array(
'var' => $var,
'column2' => 123
)
);
if ($qry) {
$new_record = $wpdb->insert_id;
echo 'Record was inserted successfully with an id of ' . $new_record_id;
} else {
echo "There was an error with the SQL query";
}
?>
I looked into the WordPress Codex and it says not to escape these values but I want to ensure that I'm not leaving myself open to SQL injections. Am I protected or is there anything else I need to do? Do I have to worry about Select statements as well?
Here is the Codex for reference: http://codex.wordpress.org/Class_Reference/wpdb
Thanks in advance!
You are safe, the WPDB will sanitize the data for you.
Related
This question already has answers here:
How can prepared statements protect from SQL injection attacks?
(10 answers)
Closed 5 years ago.
After my prior post PHP Looping through elements and adding to Database
I went back to the drawing board, as I'm terrified of injection and would like some advice. Is the below "safe":
$stmt = $conn->prepare("INSERT INTO responses (skey, rtext) VALUES (?, ?)");
$stmt->bind_param("is", $skey, $rtext);
$skey = 1;
$rtext = mysqli_real_escape_string($conn,$_POST['Q1Answer']);
if(!$stmt->execute()){trigger_error("there was an error....".$con->error, E_USER_WARNING);}
$skey = 2;
$rtext = "Hello";
if(!$stmt->execute()){trigger_error("there was an error....".$con->error, E_USER_WARNING);}
$stmt->close();
$conn->close();
I could also call the below function:
function SanitizeForSQL($str)
{
if( function_exists( "mysql_real_escape_string" ) )
{
$ret_str = mysql_real_escape_string( $str );
}
else
{
$ret_str = addslashes( $str );
}
return $ret_str;
}
Would any of the above help prevent injection?
When you bind variables to placeholder values your job is done. mysqli or PDO or whatever database driver you're using is responsible for safely encoding it from that point forward.
There is absolutely no need to add more sanitization functions on top of that, and many of these do more harm than good. The most important thing should be ensuring you never put unescaped data in your query, and the safest way to do that is to be extremely disciplined about using placeholder values.
If you pre-escape things then you'll have to un-escape them later. Data you assume is going to be used in HTML may very well turn up in a JSON document when you add an API to something, so now it has to be de-HTML-escaped before properly JSON escaping. This is really obnoxious.
Keep the data in your database as neutral as possible. That is, as raw as you can manage. If you're using Markdown, for example, keep the raw markdown in the database. If you're allowing certain HTML tags, put in whatever the user put in and scrub with a white-lister later. You may want to change your rules in the future to be more relaxed, and if you've already purged that content it's gone forever.
You should use access token. For each database access you should define access token for client and without token query won't be executed.
You can define your function like this:
function SanitizeForSQL($str,$token)
{
if($token=="Predefined token")
{
if( function_exists( "mysql_real_escape_string" ))
{
$ret_str = mysql_real_escape_string( $str );
}
else
{
$ret_str = addslashes( $str );
}
return $ret_str;
}
else return "Token is invalid".
}
So I'm making my own blog scripts using MYSQL and PHP.
I had the whole 'writing the blog to a database' thing working perfectly, until I realised that if you tried to write a blog with speech marks, this would prevent the INSERT statement from working (obviously - the speechmarks were ending the SQL statement).
So I tried to use real_escape_string, and now the INSERT doesn't work even if you exclude quotes.
I tried using:
sqlstate
in order to find out the issue, and it returned "42000" - which, after googling for a little bit, refers to a syntax error, which doesn't make much sense as there is no syntax error before the use of real_escape_string.
Also, I'm now getting this error:
Call to a member function close() on a non-object in /postarticle.php on line 37
Which refers to the close() call in the ELSE statement.
Please may you help? Been going round in circles for a while. Here is my code:
<?php
$host = 'CENSORED';
$user = 'CENSORED';
$pass = 'CENSORED';
$db = 'CENSORED';
$connection = new mysqli($host,$user,$pass,$db);
$_SESSION["article"] = $_POST["article"];
$date_of_blog = getdate();
$article = ($_SESSION["article"]);
$sql1 = "SELECT * FROM `Blogs`";
$res1 = $connection->query($sql1);
$newrows = $res1->num_rows + 1;
$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES ('$newrows','$article','$date_of_blog')";
$sql2 = $connection->real_escape_string($sql2);
$res2 = $connection->query($sql2);
if ($res2->num_rows == $newrows)
{
$res->close();
$connection->close();
header( 'Location: adminpanel.php' );
}
else
{
echo ($connection->sqlstate);
$connection->close();
$res->close();
}
exit();
?>
Also, on a side note, the getdate() call that I've got has never worked. In the database every blog post comes up as:
0000:00:00 00:00:00
EDIT:
Issue is now solved. Find the functional code below:
<?php
$host = 'CENSORED';
$user = 'CENSORED';
$pass = 'CENSORED';
$db = 'CENSORED';
$connection = new mysqli($host,$user,$pass,$db);
$_SESSION["article"] = $_POST["article"];
$article = ($_SESSION["article"]);
$article = $connection->real_escape_string($article);
$sql1 = "SELECT * FROM `Blogs`";
$res1 = $connection->query($sql1);
$newrows = $res1->num_rows + 1;
$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES (\"$newrows\",\"$article\",CURDATE())";
$res2 = $connection->query($sql2);
if ($res2 != false)
{
header( 'Location: adminpanel.php' );
}
else
{
echo ($connection->sqlstate);
}
$connection->close();
$res->close();
exit();
?>
I'm very sorry if these questions are basic and annoy the professionals around here; I've tried to follow the guidelines and I've googled for a while etc. I just haven't found any solutions that match my issue(s).
Thankyou for your time.
There are a number issues with the code as originally posted. Chiefly, the cause of the two issues you initially identified is a misuse of mysqli::real_escape_string(). It needs to be called on each variable individually which appears in the code. So instead of calling it on the whole statement, it must be called multiple times for multiple variables like:
$article = $connection->real_escape_string($connection);
The failure of the query due to incorrect quoting (due to real_escape_string()) is the reason for the error message calling close().
As ascertained in the comments, you are using num_rows + 1 to validate that one new row has been inserted based on the previous number of rows returned. This is problematic for a few reasons. Mainly, it exposes a race condition wherein a row may be inserted from two sessions at once and one or both will fail because the expected value for $newrows doesn't match. Really BlogID should be an auto_increment column in your database. That eliminates the need for any logic around it whatsoever. You don't even need to include it in the INSERT because it will be automatically incremented.
That also completely eliminates the need for the first SELECT statement.
Substituting MySQL's native NOW() function for the date value, you can simplify the statement to:
INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES ('$article', NOW())
To test success or failure of the insert, you just need to verify that its variable is not false.
Putting this together, your code can be reduced as:
if (!isset($_POST['article'])) {
// exit or handle an empty post somehow...
}
$connection = new mysqli($host,$user,$pass,$db);
$_SESSION["article"] = $_POST["article"];
// Escape $article for later use
$article = $connection->real_escape_string($_SESSION["article"]);
// Only an INSERT is needed. $article is already escaped
$sql = "INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES ('$article', NOW())";
// Run the query
$res = $connection->query($sql);
// Test for failure by checking for a false value
if ($res) {
// The connection & resource closure can be omitted
// PHP will handle that automatically and implicitly.
header( 'Location: adminpanel.php' );
// Explictly exit as good practice after redirection
exit();
}
else {
// The INSERT failed. Check the error message
echo $connection->error;
}
This should bring your current code into working order. However, since you're learning this it is an excellent time to begin learning to use prepared statements via prepare()/bind_param()/execute() in MySQLi. This is a recommended best practice to prevent SQL injection, although using real_escape_string() works as long as you use it correctly and never forget.
See How can I prevent SQL injection in PHP for examples.
But it would look like:
// connection already established, etc...
// Prepare the statement using a ? placeholder for article
$stmt = $connection->prepare("INSERT INTO Blogs (Blog_Contents, D_O_B) VALUES (?, NOW())");
if ($stmt) {
// bind in the variable and execute
// Note that real_escape_string() is not needed when using
// the ? placeholder for article
$stmt->bind_param('s', $_SESSION['article']);
$stmt->execute();
// Redirect
header( 'Location: adminpanel.php' );
exit();
}
else {
echo $connection->error;
}
You need to apply the real_escape_string function to the variables not the entire SQL string.
$sql2 = "INSERT INTO Blogs(BlogID, Blog_Contents, D_O_B) VALUES ('".$connection->real_escape_string($newrows)."','".$connection->real_escape_string($article)."','".$connection->real_escape_string($date_of_blog)."')";
The purpose is to remove anything that might be misinterpreted as query functions by MySQL, but there are parts of the query that you obviously want to be interpreted as such.
I'm trying to insert some values into a database, however, it's always unsuccessful and I'm not sure what the problem is. Could I get some assistance please
$query1 = "INSERT INTO `incidenceoffire`(`locationOfFire`, `dateFireOccurred`, `timeFireOccurred`, `classOfFire`, `originOfFire`, `noOfWounded`,
`noOfFatalities`,`occupancy`,`noOfFirePersonnelOnScene`,`noOfFireTrucks`,`backupUsed`)
VALUES('$locationoffire', '$datefireoccurred', '$timefireoccurred', '$classoffire', '$originoffire', '$occupancy', '$noofwounded', '$nooffatalities',
'$noofpersonnel', '$nooftrucks', '$backuptrucks')";
$incidenceoffire_id = mysql_insert_id();
$query2 = "INSERT INTO `backuptrucks` (`unitName`) VALUES ('$unitname')";
$query2 .=" WHERE `IncidenceOfFire_incidentID` = '".$incidenceoffire_id."'";
$result = false;
if(mysql_query('BEGIN')){
if(mysql_query($query1) && mysql_query($query2))
{
$result = mysql_query('COMMIT');
echo '<script type="text/javascript">
alert("Insert Successful!");
</script>';
}
else
{
mysql_query('ROLLBACK');
echo '<script type="text/javascript">
alert("Insert Unsuccessful!");
</script>';
}
}
For the purpose of clarity, here's what you need. I'm not going to optimize it or anything but it's a baseline for where you should start.
$mysqli = new mysqli('host', 'name', 'user', 'database');
$query1 = $mysqli->prepare('INSERT INTO
`incidenceoffire`(
`locationOfFire`,
`dateFireOccurred`,
`timeFireOccurred`,
`classOfFire`,
`originOfFire`,
`noOfWounded`,
`noOfFatalities`,
`occupancy`,
`noOfFirePersonnelOnScene`,
`noOfFireTrucks`,
`backupUsed`
)
VALUES(?,?,?,?,?,?,?,?,?,?,?));
In the above we're using mysqli's prepare. This function will allow us to safely escape the data that is being passed into the query. This is for security purposes. the ? represents the value that we're inserting associated with the field's we've identified above.
$query1->bind_param('sssssssssss',
$locationoffire,
$datefireoccurred,
$timefireoccurred,
$classoffire,
$originoffire,
$occupancy,
$noofwounded,
$nooffatalities,
$noofpersonnel,
$nooftrucks,
$backuptrucks);
Here, we've used bind_param to bind the variable to the ?'s that we've used in the prepared statement. This allows us to safely escape the data. the s in the first argument stands for string as the data we expect to receive from that variable should be a string. You can also use i for integer - when expecting only numbers, and d for double, if you expect to have .'s and ,'s. Lastly, you can use b if you expect a blob to be transferred over time to the statement.
Now, because you have two statements, repeat the above and use $query2, then you can perform your conditional. We will use execute() to execute the prepared statement we built earlier.
if($query1->execute() && $query2->execute()):
$result[] = $query1->commit();
$result[] = $query2->commit();
echo '<script type="text/javascript">alert("Insert Successful!");</script>';
else:
$result[] = $query1->rollback();
$result[] = $query2->rollback();
echo '<script type="text/javascript">alert("Insert Unsuccessful!");</script>';
endif;
This should all function for you from the get go, but please read and understand what's being relayed. Always use documentation and examples provided at http://php.net, and please follow best practices for security less you create a site that becomes hacked and I end up repairing it one day
I wonder if anybody can help me. I'm trying add some data to a table within a wordpress database. I'm using the $wpdb class and every time I run the code, the validation I got in place comes back positive. But when I check the table in the database, it's empty.
My Code:
// Define global $wpdb
global $wpdb;
// Retrieve information from the form
$location = $_POST['txtLocation'];
$price = $_POST['txtPrice'];
$type = $_POST['sltType'];
$revenue = $_POST['txtRevenue'];
$surgeries = $_POST['txtSurgeries'];
$tenure = $_POST['sltTenure'];
$upload = $_POST['txtUpload'];
// SQL statement to insert information from the form into the database
$sql = $wpdb->prepare("INSERT INTO practices ('Location', 'Price', 'Type', 'Revenue', 'No. of Surgeries', 'Tenure', 'PDF') VALUES (%s,%s,%s,%s,%s,%s,%s", $location, $price, $type, $revenue, $surgeries, $tenure, $upload);
$result = $wpdb->query($sql);
if (!result) {
echo '<p class="sql-error">There was a problem uploading the practice to the database</p>';
} else {
echo '<p class="sql-success">The practice was uploaded to the database</p>';
}
echo $wpdb->last_query;
Does anybody know why my code is doing this???
Thanks
Forgot a closing ) in your query.
Here
VALUES (%s,%s,%s,%s,%s,%s,%s
^
Try:
$sql = $wpdb->prepare("INSERT INTO practices ('Location', 'Price', 'Type', 'Revenue', 'No. of Surgeries', 'Tenure', 'PDF') VALUES (%s,%s,%s,%s,%s,%s,%s)", $location, $price, $type, $revenue, $surgeries, $tenure, $upload);
Also it is
if (!$result) {
^
In addition to closing the VALUES statement in your query as has been suggested, you might find that referencing the column names using ' would also give you issues.
MySQL expects column identifiers to be enclosed using backticks: `.
I have this query:
mysql_query("INSERT INTO `63_Activity` (`username`, `time`) VALUES (`$usernann2`, `$date`)");
However, it doesn't do anything. Even when I tried correcting the variables to
". $variable ."
I checked the variables.
I copied the little line of code from somewhere it works.
The database and tables are existing.
I just thought I had things under control, then that happened -.-
Thank you in advance.
PS: I tried adding or die() - but no error. Nothing.
Values need to be in single quotes ('), not backticks (`)
mysql_query("INSERT INTO `63_Activity` (`username`, `time`) VALUES ('$usernann2', '$date')");
You should also make sure you're sanitizing your inputs, as well as preferably not using the mysql_ functions in place of mysqli_
You would be better off using Paramaterized queries as in the following example:
<?php
try {
$usernann2 = "whateverUsernameFits";
$date = new DateTime('2000-01-01');
$stmt = $this->db->prepare ( "INSERT INTO 63_Activity (username, time) VALUES (:usernann2, :date)");
$stmt->bindParam ( ':usernann2', $usernann2 );
$stmt->bindParam ( ':date', $date );
$stmt->execute ();
}catch ( PDOException $e )
{
throw new Exception ( $this->db->errorInfo () . 'Problem inserting object ' );
} catch ( Exception $e ) {
throw new \Exception ( 'Problem inserting object ' );
}
?>
Bound parameters are a staple in preventing SQL Injection attacks. The exceptions thrown would give you a clue as to what might be the problem in your query if there is one. I normally check the query first to make sure it's working with real values. From there it is a process of elimination.
PS. https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet for more information on SQL Injection. You should also be able to find some excellent information and questions here on Stackoverflow regarding SQL Injection.
try to put the query in a variable and echo it, and see if anything wrong, try to run it on php my admin also