Print MySQL Output Based on Query String Value - php

I'm trying to take a query string param such as ?table=products and have mysql return all the rows for the "products" table in mysql. I tried running the code below in my browser, but I just get a blank white page. I know the mysql server/username/pass information is correct, I've tested the query in mysql and it works fine.
I guess I have two question:
What am I doing wrong?
How come I can't see any error messages when php has an issue?
e.g. code:
<?php
// Get query string parameter value
$keys = array_keys($_GET);
$key = $keys[0];
$value = $_GET[$key];
// Setup connection to mysql database
$serverName = "localhost";
$username = "root";
$password = "password";
$dbname = "webserver";
$conn = new mysqli($serverName, $username, $password, $dbname);
// SQL query
$sql = "SELECT * FROM $value";
$result = $conn->query($sql);
// Print results
echo $result;
?>

Follow the instuctions on below link to enable php.ini errors
How do I get PHP errors to display?
VULNERABLE IMPLEMENTATION WARNING
The above comments clearly mention the side effects of this implementation.
Since knowing the actual bug is a developer's right! Continue reading the answer keeping the safety of software and its users in mind.
You are trying to print $result which is not valid since its an object.
You can do the following instead:
$response = array();
$sql = "SELECT * FROM $value";
$result = $conn->query($sql);
// Print results
if ($result) {
while($row = $result->fetch_array(MYSQL_ASSOC)) {
$response[] = $row;
}
}
echo json_encode($response);

What am I doing wrong?
Sadly, pretty much everything.
// Get query string parameter value
$keys = array_keys($_GET);
$key = $keys[0];
$value = $_GET[$key];
You are dereferencing a named value based on its position. And its totally unnecessary. Consider:
$value=$_GET['table'];
...
$conn = new mysqli($serverName, $username, $password, $dbname);
Where is your error checking to see if $conn was initialized?
$result = $conn->query($sql);
again, no error checking.
echo $result;
$result here is a mysqli_result object. You need to call some methods on it to get the data out.
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
var_export($row);
}
How come I can't see any error messages when php has an issue?
Have you tested that the default handlers produce output in your browser? You're not overriding the config in php.ini in the code you've shown us. Did you check your logs?
ini_set('diplay_error', 1);
error_reporting(E_ALL);
I just get a blank white page
Would it be so hard to put
print "finished";
at the end of the code? Then you'd at least know if the code executed.

The main issue you have right now is you need to get the results
while ($row = $result->fetch_assoc()) {
//do something with row
}
See ( for mysqli->query method )
http://php.net/manual/en/mysqli.query.php
false on failure and mysqli_query() will return a mysqli_result object on success
See ( for the result objects definition )
http://php.net/manual/en/class.mysqli-result.php
Now as others mentioned I would never just concatenate user data into your query. Imagine a hacker knows the name of a valid table, not hard considering your sending it through the request. All they would have to do is send a value like this:
$value = 'real_table; DROP DATABASE';
And your query becomes.
$sql = "SELECT * FROM real_table; DROP DATABASE";
I won't say that this would actually work as there are ( maybe ) some restrictions on running multiple queries in a single request,user permissions etc... That might save your bacon, but I certainly wouldn't risk it.
So you have 2 choices.
Use a white list of tables
Query the DB for the schema
The first one is easy to do, make a list of tables
$whitelist = [
'table1',
'table2'
];
Then compare your user input
$safeTable = false;
if( false !== ($index = array_search($table, $whitelist))) {
$safeTable = $whitelist[$index];
}else{
//log error and
exit();
}
// SQL query
$sql = "SELECT * FROM $safeTable";
$result = $conn->query($sql);
For the second one,
$schema = $conn->query('SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` LIKE "database"');
$whitelist = [];
while ($row = $result->fetch_assoc()) {
$whitelist[] = $row['TABLE_NAME'];
}
$safeTable = false;
if( false !== ($index = array_search($table, $whitelist))) {
$safeTable = $whitelist[$index];
}else{
//log error and
exit();
}
// SQL query
$sql = "SELECT * FROM $safeTable";
$result = $conn->query($sql);
This will return a list of all the tables in that database, from which you can build an array and then compare. The nice thing about the second one is that if you add a table then you don't have to change the code, which may or may not be a good thing. You have to have a user with permission to read from information_schema database. And you have to do an additional query.
-note- I am not directly using the users input, I'm using their input to find my data. It's less prone to breaking when there is a coder error. Consider this:
///all my codes are broken;
--if(!in_array($_GET['table'], $whitelist))) {
-- //log error and
-- exit();
--}
// SQL query
$sql = "SELECT * FROM {$_GET['table']}";
$result = $conn->query($sql);
Against this:
$safeTable = false;
// all my codes are broken
-- if( false !== ($index = array_search($_GET['table'], $whitelist))) {
-- $safeTable = $whitelist[$index];
-- }else{
-- //log error and
-- exit();
-- }
// SQL query
$sql = "SELECT * FROM $safeTable"; //$safeTable is undefined or false;
$result = $conn->query($sql);
Were using our code for inclusion, instead of exclusion. So if it breaks, it's never included. The other way, if it breaks it's never excluded. Which is not a situation we want to be even remotely possible.
I hope that helps you understand some of the pitfalls. The #1 rule for SQL (or anything on the web), is Never Trust the User. Never put their data into your SQL.

Related

Real_escape_string prevents INSERT statement from working MYSQL PHP

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.

Unable to get value from a PDO array

I am fairly new to PHP and have been following the Lynda.com tutorials (although they still use mysql in stead of mysqli or PDO).
I'm having problems with using the data I get from my queries.
I'll use my login page as example, leaving out the connect to db part:
$login_username = trim(htmlspecialchars($_POST['username']));
$password = trim(htmlspecialchars($_POST['password'])); // from login form
$stmt = $db->prepare("SELECT * FROM users
WHERE username = :login_username");
$stmt->bindParam(':login_username', $login_username);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if($stmt->rowCount() > 0 && $result = password_verify($password,$result['hashed_password'])) {
$_SESSION['logged_in_id'] = $result['id'];
$_SESSION['logged_in_username'] = $login_username; //this was the only way I could pass the username as I could not get it from $result['username']
$_SESSION['first_name'] = $result['first_name'];
$_SESSION['last_name'] = $result['last_name'];
Nothing gets passed to the session and there are no errors. I also can't echo out the value of $result. If I just try to echo $result, then I just get the value 1
Please help!
Your problem is:
... && $result = password_verify($password,$result['hashed_password'])
Note that $result is an array that contains the row that you just fetched and you are assigning it a new value here; you are overwriting your $result variable so all assignments afterwards will fail.
You probably want something like:
... && password_verify($password,$result['hashed_password'])
Also note that you should not rely on the rowCount() as that is not necessarily what you expect for a SELECT statement.
As you are fetching a row already, you can simply do:
if ($result && password_verify($password,$result['hashed_password']))
If there is no result, the second condition will never be checked so it will not lead to warnings or errors.

return Json array from PHP mySQL - Error

Here is my php code and i want to get JSON array relevant to to my $_GET("ProductGroup") variable. but when i log in to the page by submitting parameters via URL
http://iilsfa.br0s.info/SFA/get_all_products.php?%27Laptops%27 it displays this error.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
i could not find where the error is. please help
<?php
include_once './db_connect.php';
function getProducts(){
$db = new DB_CONNECT();
// array for json response
$response = array();
$response["products"] = array();
// Mysql select query
// check for post data
if (isset($_GET["ProductGroup"])) {
$selected_group = $_GET['ProductGroup'];
}
$result = mysql_query("SELECT * FROM Product WHERE ProductType= $selected_group")or die(mysql_error());
while($row = mysql_fetch_array($result)){
// temporary array to create single category
$tmp = array();
$tmp["id"] = $row["ProductID"];
$tmp["name"] = $row["ProductName"];
$tmp["type"] = $row["ProductType"];
$tmp["image"] = $row["ProductImage"];
$tmp["des"] = $row["ProductDescription"];
// push category to final json array
array_push($response["products"], $tmp);
}
// keeping response header to json
header('Content-Type: application/json');
// echoing json result
echo json_encode($response);
}
getProducts();
?>
PS- I have changed my sql query to this
$result = mysql_query("SELECT * FROM Product WHERE ProductType= '".mysql_real_escape_string('$selected_group')."'")or die(mysql_error());
Now its not displaying the previous error.but showing an empty json string. i have checked SQL query with phpmyAdmin.it working correctly and produce results..
try this below. Dont use simple query. See SQL injection. SQL Injection use mysql_real_escape_string to prevent from sql injection
<?php
include_once './db_connect.php';
function getProducts(){
$db = new DB_CONNECT();
// array for json response
$response = array();
$response["products"] = array();
// Mysql select query
// check for post data
if (isset($_GET["ProductGroup"])) {
$selected_group = $_GET['ProductGroup'];
}
$result = mysql_query("SELECT * FROM Product WHERE ProductType= '".mysql_real_escape_string($selected_group)."'")or die(mysql_error());
while($row = mysql_fetch_array($result)){
// temporary array to create single category
$tmp = array();
$tmp["id"] = $row["ProductID"];
$tmp["name"] = $row["ProductName"];
$tmp["type"] = $row["ProductType"];
$tmp["image"] = $row["ProductImage"];
$tmp["des"] = $row["ProductDescription"];
// push category to final json array
array_push($response["products"], $tmp);
}
// keeping response header to json
header('Content-Type: application/json');
// echoing json result
echo json_encode($response);
}
getProducts();
?>
Quote the parameters...
$result = mysql_query("SELECT * FROM Product WHERE ProductType= '$selected_group'")or die(mysql_error());
You must not use mysql_*.
Also look at mysqli_real_escape_string
First, your code is well documented, even for a starter, not too much, just only on places where it's required.
Now, your error lies in the query itself.
$result = mysql_query("SELECT * FROM Product WHERE ProductType= $selected_group")or die(mysql_error());
If a field in the query is of type string, you must add single quotes around it, or the interpreter will think that you're looking for a column name. Here below is a solution.
$result = mysql_query("SELECT * FROM Product WHERE ProductType= '$selected_group'")or die(mysql_error());
But i would add that your query is not secure. It allows SQL injection, which can harm your database data. for further information about SQLi, please refer to this page
You can go for mysqli() functions, which is an improved version of the mysql()-functions. Although i recommend to use PDO instead.

Jquery Autocomplete remote datasource

Im trying to use Jquery UI's autocomplete feature to query usernames on my database. So the user enters a username similar to one on my db and the autocomplete is suppossed to guess what they are looking for in a drop down. Unfortunately, I can't get the backend script to return suggestions.
<?php
sleep( 3 );
// no term passed - just exit early with no response
if (empty($_GET['term'])) exit ;
$q = strtolower($_GET["term"]);
// remove slashes if they were magically added
if (get_magic_quotes_gpc()) $q = stripslashes($q);
$sql = "SELECT * FROM users";
$r = mysql_query($sql);
$items = array();
if ( $r !== false && mysql_num_rows($r) > 0 ) {
while ( $a = mysql_fetch_assoc($r) ) {
$username = $a['username'];
array_push($items, $username);
}
}
$result = array();
foreach ($items as $key=>$value) {
if (strpos(strtolower($key), $q) !== false) {
array_push($result, array("id"=>$k, "label"=>$key, "value" => strip_tags($key)));
}
if (count($result) > 11)
break;
}
// json_encode is available in PHP 5.2 and above, or you can install a PECL module in earlier versions
echo json_encode($result);
/* echo $items; */
?>
The script simply returns an empty array, even when it should return a result. I have no idea what is wrong here..
First let me say, querying the database and returning the entire table to sift through for your results is a poor method. The SQL queries will execute faster if they are filtering the data from the database. You have to call up the data anyways, why not filter it and return only the relevant results?
You need to send the query a Like parameter as in the following:
$sql = "SELECT * FROM users where username like :term";
(I'm using parameterized queries in this case which you should use to protect against SQL Injection attacks.)
You can also use the more precarious method as follows:
$sql = "SELECT * FROM users WHERE username = ". $term;
Reference for Parameterized Queries:
How can I prevent SQL injection in PHP?

About the mysql_query -> mysql_fetch_array() procedure

Sample code:
$infoArray = array();
require_once("connectAndSelect.php");
// Connects to mysql and selects the appropriate database
$sql = "SOME SQL";
if($results = mysql_query($sql))
{
while($result = mysql_fetch_array($results, MYSQL_ASSOC))
{
$infoArray[] = $result;
}
}
else
{
// Handle error
}
echo("<pre>");
print_r($infoArray);
echo("</pre>");
In this sample code, I simply want to get the result of my query in $infoArray. Simple task, simple measures... not.
I would have enjoyed something like this:
$sql = "SOME SQL";
$infoArray = mysql_results($sql);
But no, as you can see, I have two extra variables and a while loop which I don't care for too much. They don't actually DO anything: I'll never use them again. Furthermore, I never know how to call them. Here I use $results and $result, which kind of represents what they are, but can also be quite confusing since they look so much alike. So here are my questions:
Is there any simpler method that I
don't know about for this kind of
task?
And if not, what names do you
give those one-use variables? Is
there any standard?
The while loop is really only necessary if you are expecting multiple rows to be returned. If you are just getting one row you can simply use mysql_fetch_array().
$query = "SOME SQL";
$result = mysql_query($query);
$row = mysql_fetch_array($result);
For single line returns is the standard I use. Sure it is a little clunky to do this in PHP, but at least you have the process broken down into debug-able steps.
Use PDO:
<?php
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
$dbh = new PDO("mysql:host=$hostname;dbname=mysql", $username, $password);
$sql = "SELECT * FROM myTable";
$result = $dbh->query($sql)
//Do what you want with an actual dataset
}
catch(PDOException $e) {
echo $e->getMessage();
}
?>
Unless you are legacied into it by an existing codebase. DONT use the mysql extension. Use PDO or Mysqli. PDO being preferred out of the two.
Your example can be come a set of very consise statements with PDO:
// create a connection this could be done in your connection include
$db = new PDO('mysql:host=localhost;dbname=your_db_name', $user, $password);
// for the first or only result
$infoArray = $db->query('SOME SQL')->fetch(PDO::FETCH_ASSOC);
// if you have multiple results and want to get them all at once in an array
$infoArray = $db->query('SOME SQL')->fetchAll(PDO::FETCH_ASSOC);
// if you have multiple results and want to use buffering like you would with mysql_result
$stmt = $db->query('SOME SQL');
foreach($stmt as $result){
// use your result here
}
However you should only use the above when there are now variables in the query. If there are variables they need to be escaped... the easiest way to handle this is with a prepared statement:
$stmt = $db->prepare('SELECT * FROM some_table WHERE id = :id');
$stmt->execute(array(':id' => $id));
// get the first result
$infoArray = $stmt->fetch(PDO::FETCH_ASSOC);
// loop through the data as a buffered result set
while(false !== ($row = $stmt->fetch(PDO::FETCH_ASSOC))){
// do stuff with $row data
}

Categories