How to sanitize query to accept table name as parameter PDO php - php

I have the following code which works, however, now i'm just wondering if this will be secure, and not sql injectable etc.
I cannot take the approach of whitelisting table names as for my web application, more tables will be generated randomly in the future and will be passed through a URL parameter of table, so therefore I will never know all of the tables.
I use the first query to determine if the table which is passed through the URL parameter actually exists, otherwise I will be exiting the script which i'm yet to add.
$db = "database1";
$table = $_GET['table'];
$stmt = $auth_table->runQuery("Select table_name, table_schema from information_schema.tables where table_schema = :db and table_name = :tablename");
$stmt->execute(array(":db"=>$db,":tablename"=>$table));
$tableRow=$stmt->fetch(PDO::FETCH_ASSOC);
$table_schema = $tableRow['table_schema'].".".$tableRow['table_name'];
$stmt = $auth_table->runQuery("Select * from ".$table_schema."");
$stmt->execute();
$testing=$stmt->fetch(PDO::FETCH_ASSOC);
print_r($testing['level']);
exit();

Whitelist your existing tables
$db = "database1";
$table = $_GET['table'];
$dbh = new PDO('mysql:host=localhost;dbname=database1', $user, $pass);
$tableSql = "SHOW TABLES FROM " . $db;
$tableRes = $dbh->query($tableSQL);
$tableArr = $tableRes->fetch(PDO::FETCH_ASSOC);
$whitelist = $tableArr[0]['Tables_in_database1'];
if(!in_array($table, $whitelist)){
exit(); //Or RickRoll Them
}

Related

How to avoid ' in the sql query?

I am using MYSQL 5.5. May I know how to avoid ' in the SQL query? Below is my example query:
<?php
$mySelect1 = "'Ahli Majlis'";
$bracket_mySelect1 = "($mySelect1)";
SQL = "SELECT * FROM user where 1 and nama_gelaran in ".$bracket_mySelect1."";
?>
The wrong result I have checked in the console log data is SELECT * FROM user where 1 and nama_gelaran in ('Ahli Majlis')
Actually I want the result is SELECT * FROM user where 1 and nama_gelaran in ('Ahli Majlis')
What I tried, but it doesn't work:
SQL = SELECT * FROM user where 1 and nama_gelaran in ".html_entity_decode(htmlentities($bracket_mySelect1,ENT_QUOTES),ENT_QUOTES); .";
I am unsure how you are executing the queries but it is best practice to use PDO rather then the mysql_ functions. This also improves security and protects against mysql injection.
Below is a sample of how to connect and run a PDO query. I have changed your IN to be = as it looks like you are only passing one value.
$dsn = "mysql:host=localhost;dbname=mydb";
$user = "dbUsername";
$passwd = "dbPassword";
$name = 'Ahli Majlis';
$pdo = new PDO($dsn, $user, $passwd);
$stm = $pdo->query("SELECT * FROM `user` WHERE nama_gelaran = :name");
$stm->bindParam('name', $name);
$user = $stm->fetch();
print_r($user);
' does not come from MySQL unless that is what you INSERTed. It does come from certain PHP functions such as htmlentities. It is '.

Unsuccessful Updating table with PHP using Update table Set

I have a table buildtracker connected to a form. I am attempting to edit the data in a particular row and therefore Update the row.
The table consists of these columns: ID key, EnteredBy, INI
$user = 'root';
$pass = '';
$db = 'cl_db';
$conn = new mysqli('localhost', $user, $pass, $db) or die("Something bad happened.");
$rowID = $conn->real_escape_string($_POST['ID']);
$enteredBy = $conn->real_escape_string($_POST['enteredBy']);
$ini = $conn->real_escape_string($_POST['ini']);
$query = "UPDATE buildtracker SET
EnteredBy = '$enteredBy', INI = '$ini'
WHERE ID = '$rowID' ";
$success = $conn->query($query); //insertion above ^ is the column names
if (!$success) {
die("Couldn't enter data: ".$conn->error);
}
return $query;
I'm receiving no new data or updates on the table. What can I do differently to improve upon this?
Thanks!
I don't know in what context this code is, in your application.
But is is [highly] recommended to use prepared statements for protection against any SQL injection attacks, especially direct data from $_POST is used (can be sanitized).
Checking whether query is executed or not in prepared statements is via $stmt->execute().
$user = 'root';
$pass = '';
$db = 'cl_db';
$conn = new mysqli('localhost', $user, $pass, $db) or die("Something bad happened.");
$prepare_query = "UPDATE buildtracker SET EnteredBy=?, INI=? WHERE ID=?";
$success = $conn->query($prepare_query); //insertion above ^ is the column names
if ($stmt = $conn->prepare($query)) {
// Possible data sanitation can be done below
$rowID = ($_POST['ID']);
$enteredBy = ($_POST['enteredBy']);
$ini = ($_POST['ini']);
// bind parameters
$stmt->bind_param('ssi', $enteredBy, $ini, $rowID);
// CHECKING is here: execute query (or die)
// Can check also for ($stmt->affected_rows > 0)
if (!$stmt->execute()){
die("Couldn't enter data: ".$conn->error);
}
return $query;
}
And using PDO instead of MySQLi would probably be better recommended.
As all errors that I create - this was a result of the declaration of my $rowID not pointing the correct html element.
original
$rowID = $conn->real_escape_string($_POST['ID']); // ID is the COLUMN NAME
Working
$rowID = $conn->real_escape_string($_POST['rowID']); //rowID is the DOM element name
Thank you for your comments and answers.
Going to look into implementing Hossam's safer version of preventing injections. I appreciate you all :)

how to optimize database connections for nested queries?

I am using nested queries to retrieve information from multiple tables. I need advice on optimizing this php code.
This function creates an object.
public function conn($query){
$mysqli = new mysqli('test','test','test','test');
$result = $mysqli->query("SET NAMES utf8");
$result = $mysqli->query("set character_set_client='utf8'");
$result = $mysqli->query("set collation_connection='utf8_general_ci'");
$result = $mysqli->query($query);
$mysqli->close();
return $result;
}
This code uses that function.
$connect = $this->conn("SELECT * FROM Table LIMIT 100000");
while($i = $connect->fetch_assoc()){
$name = $i["name"];
$connect2 = $this->conn("SELECT * FROM Names WHERE Name = '$name'");
if($connect2 ->num_rows > 0){
echo $name.'<br>';
}
}
Need recommendations for a connection to the database.
In the while loop, as you see, I am checking for the presence of $name in other table. But I am opening and closing a connection every time through the loop. And this will be 100001 connection opens and closes.
Is it possible to open a connection to the database only once?
P.S.: The SQL is an example - Please don't suggest changes there, because I am trying to figure out how to handle the repeated queries, not optimize the SQL.
Connection objects are reusable. Make a connection, then use it to make as many queries as you want. Close each query (that is, each result set) when you're done with it, then close the connection at the end of the run.
Closing a connection is a network operation, so it takes a while. Closing a query is mostly an in-memory operation, so it is faster.
In your example, you're using nested queries (more on that in a moment). Your code should end up looking something like this pseudocode:
public function getconn(){
$mysqli = new mysqli('test','test','test','test');
$mysqli->query("SET NAMES utf8");
$mysqli->query("set character_set_client='utf8'");
$mysqli->query("set collation_connection='utf8_general_ci'");
return $mysqli; /* return the connection handle */
}
$conn1 = getconn();
$conn2 = getconn();
$resultset1 = $conn1->query("SELECT * FROM Table LIMIT 100000");
while($i = $resultset1->fetch_assoc()){
$name = $i["name"];
$resultset2 = $conn2->query("SELECT * FROM Names WHERE Name = '$name'");
if($resultset2->num_rows > 0){
echo $name.'<br>';
}
$resultset2->close();
}
$resultset1->close();
$conn1->close();
$conn2->close();
(Please note; I haven't debugged this code.)
To take this optimization one step further, you should consider using a prepared statement for the query inside the while loop. Here's documentation on that http://php.net/manual/en/mysqli-stmt.fetch.php.
$conn1 = getconn();
$conn2 = getconn();
/* create a prepared statement with placeholder parameter ? */
$stmt = $mysqli->prepare("SELECT * FROM Name WHERE Name = ?"));
$name = '';
$name_out = '';
$stmt->bind_param("s", $name);
$stmt->bind_result($name_out);
$resultset1 = $conn1->query("SELECT * FROM Table LIMIT 100000");
while($i = $resultset1->fetch_assoc()){
$name = $i["name"];
$resultset2 = $stmt->execute(); /* run query with bound parameter */
if ($stmt_fetch() ( {
echo $name.'<br>';
}
$resultset2->close();
}
$resultset1->close();
$conn1->close();
$conn2->close();
(Please note; I haven't debugged this code either.)
Now, it's possible your pair of queries are just an example to show a set of nested queries. If so, that's fine. But, you are performing this task (retrieve 100K names) in an almost unimaginably inefficient way. You've said you don't want anybody to rewrite this query, but I am sorry, I can't just let this one pass.
This code would do a far more streamlined job.
$conn = getconn();
$q = "SELECT t.name FROM Table t JOIN Name n ON t.name = n.name LIMIT 100000";
$resultset = $conn->query($q);
while($i = $resultset->fetch_assoc()){
$name = $i["name"];
echo $name.'<br>';
}
$resultset->close();
$conn->close();
It's more efficient for two reasons. First, it doesn't use SELECT *, which ends up sending all sorts of data over the network from your MySQL server to your php program, just to throw it away.
Second, it doesn't use the nested queries. Instead, the JOIN query pulls all the name columns from Table that have a matching name column in Names.

How do I input values into a mysql table using php when there is a restriction?

I'd like to insert data into a table only when certain values in that table's (sessionid) row match another variable. I am struggling to put together the INSERT statement. The approach I am taking: retrieve all the rows in the table that match the criteria (retailer=$retailer) and then iterate through those rows inputting the variable options into the sessionid table.
$retailer = $_GET['retailer'];
$options = $_GET['options'];
$session = session_id();
//mysql connection stuff goes here
$query = "
SELECT *
FROM `sessionid`
WHERE `retailer` = '$retailer'
";
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_assoc($result)) {
mysql_query("INSERT INTO sessionid (options) VALUES('$options')");
}
Is the syntax correct for me to do this? Thanks!
Are you maybe looking for the UPDATE command instead?
UPDATE sessionid
SET options = $options
WHERE retailer = $retailer
By the way, I would look in to using PDO as it's more secure than pushing $_GET values in a database.
$db = new PDO('mysql:host=localhost;dbname=MYDATABASE', 'username', 'password');
$db->prepare('UPDATE sessionid SET options = ? WHERE retailer = ?');
$db->execute(array($options, $retailer));
You can use the WHERE clause in mysql to do this. If you are changing an existing row, you actually want UPDATE, not INSERT.
UPDATE sessionid SET options=$options where retailer = $retailer

PHP query single line from database

I'm having a problem echoing a single line from a sql query. I'm still pretty new at this but I can't figure it out at all.
I have a page titled "listing.php?id=7"
Inside the page is this script:
<?php
mysql_connect("localhost", "user", "pass");
mysql_select_db("table");
$query = "SELECT * FROM vehicles WHERE id='$id'";
$result = mysql_query($query);
while($r = mysql_fetch_array($result))
{
$year = $r["year"];
$make = $r["make"];
$model = $r["model"];
$miles = $r["miles"];
$pricepay = $r["pricepay"];
$pricecash = $r["pricecash"];
$transmission = $r["transmission"];
$color = $r["color"];
$vin = $r["vin"];
echo "$year $make $model $miles $pricepay $pricecash $transmission $color $vin<br />";
}
?>
The problem lies within "WHERE id='$id'". When I use a var, it displays nothing, but if I manually make it my ID number, example 7, it works fine. What's am I doing wrong?
if
SELECT * FROM vehicles WHERE id=7
works but
SELECT * FROM vehicles WHERE id='$id'
doesn't work
then get ride of the quotes around $id
So
SELECT * FROM vehicles WHERE id=$id
The quotes are turing $id into a string comparison - which won't work if the column type is integer.
Even better, use PDO. Create the connection:
$db = new PDO("mysql:host=localhost;dbname=someDBname", 'user', 'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
but do it in just one script, preferably in a singleton or somesuch. This has many advantages, including placing all database passwords in one file (which is easier to secure) and reducing the possibility of typos in the hostname, database name, username or password causing the connection to fail. Use it as:
try {
$query = $db->prepare(
"SELECT year, make, model, miles, pricepay,
pricecash, transmission, color, vin
FROM vehicles WHERE id=?"
);
$query->execute(array($_REQUEST['id']));
while ($row = $query->fetch(PDO::FETCH_NUM)) {
echo implode(', ', $row);
}
} catch (PDOException $exc) {
echo "Query failed.";
}
This uses a prepared query, which is not vulnerable to SQL injection. It also does away with "or die".
In case you haven't seen singletons, here's an example:
class DB {
private static $db;
static function open() {
if (! isset(self::$db) ) {
self::$db = new PDO('mysql:host=hostName,dbname=dbName', 'user', 'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$db;
}
}
Then, whenever you need a connection, just call DB::open(). If you need to connect to multiple hosts, store PDOs in an associative array within DB, rather than DB::$db. In this case, you could put the connection information in the DB script, or put it in a separate configuration file that DB parses.
Take your original code and add this line before the query:
$id = (int)$_GET['id']; // Sanitize Integer Input
And change your query as others suggested to remove the quotes:
$query = "SELECT * FROM vehicles WHERE id=$id";
I am assuming your id is a normal mysql auto_increment which starts at 1. That means if `$_GET['id'] is anything but a number, it will come back as 0 and thus not match anything in the database.
$query = sprintf("SELECT * FROM vehicles WHERE id=%d", mysql_real_escape_string($_GET['id']));
$result = mysql_query($query);
I hope you don't have register globals on which means you should use $_GET['id'];
You can't put quotes around the id field if it's an int in your table
Use mysql_real_escape_string to prevent sql injection

Categories