I am trying to pass a parameter to my query to filter by client (chosen through a dropdown list). I am aware of SQL injections and would like to avoid using the parameter in my query string. Is there a way I could pass my parameter to the query without having to risk an sql injection?
//Function creates a connection between a query and a db table.
function createQueryConn($query)
{
$GLOBALS['queryconn'] = sqlsrv_query($GLOBALS['conn'], $query, array(), array("Scrollable" => 'static'));
}
class CONNECTDB
{
// Creates connection to the database using sqlsrv.
public function dbConnection()
{
$servername = "bl3c7b";
$connectionInfo = array("Database" => "dashboard_das", "UID" => "test", "PWD" => "test",'ReturnDatesAsStrings'=>true);
$GLOBALS['conn'] = sqlsrv_connect($servername, $connectionInfo);
if (!$GLOBALS['conn']) {
echo "Error connecting to database.";
die(print_r(sqlsrv_errors(), true));
}
}
}
// Call DB connection.
$Connection = new CONNECTDB();
$Connection->dbConnection();
// Function gets activities per employee param.
function getActivityPerEmployee($employee)
{
$WoNum = $Status = null;
$query = "SELECT [WONUM],[STATUS] FROM [dbo].[Activities] act
WHERE YEAR(SCHEDSTART)=2019
AND
[STATUS] not like 'CLOSED'
AND
DATEDIFF(dd,SCHEDSTART,SCHEDFINISH) < 30
and OWNER like '$employee'
order by [OWNER] ASC";
createQueryConn($query);
$rows = sqlsrv_has_rows($GLOBALS['queryconn']);
if ($rows === false) {
} else {
while ($row = sqlsrv_fetch_array($GLOBALS['queryconn'], SQLSRV_FETCH_ASSOC)) {
ConvertData($row["WONUM"],$row["STATUS"]);
}}
}
PHP Driver for SQL Server supports the execution of parameterized queries by using sqlsrv_query() or sqlsrv_prepare() \ sqlsrv_execute() functions.
The sqlsrv_query function is well-suited for one-time queries and
should be the default choice to execute queries unless special
circumstances apply. This function provides a streamlined method to
execute a query with a minimum amount of code. The sqlsrv_query
function does both statement preparation and statement execution, and
can be used to execute parameterized queries.
...
The combination of
sqlsrv_prepare and sqlsrv_execute separates statement preparation and
statement execution in to two function calls and can be used to
execute parameterized queries. This function is ideal to execute a
statement multiple times with different parameter values for each
execution.
If you want to execute a parameterized query, follow the next steps:
Define a placeholders in the SQL statement using question marks (?).
Initialize an array of parameter values which correspond to parameter placeholders.
The following example is based on your code and demonstrates how to execute a parameterized query:
<php
...
function getActivityPerEmployee($employee)
{
$WoNum = $Status = null;
$query = "
SELECT [WONUM],[STATUS]
FROM [dbo].[Activities] act
WHERE
YEAR(SCHEDSTART)=2019 AND
[STATUS] not like 'CLOSED' AND
DATEDIFF(dd,SCHEDSTART,SCHEDFINISH) < 30 and
OWNER LIKE '%' + ? + '%'
-- or without LIKE
-- OWNER = ?
order by [OWNER] ASC
";
$GLOBALS['queryconn'] = sqlsrv_query(
$GLOBALS['conn'],
$query,
array($employee),
array("Scrollable" => 'static')
);
if ($GLOBALS['queryconn'] === false) {
echo "Error: ".print_r(sqlsrv_errors(), true);
exit;
}
$rows = sqlsrv_has_rows($GLOBALS['queryconn']);
if ($rows === false) {
} else {
while ($row = sqlsrv_fetch_array($GLOBALS['queryconn'], SQLSRV_FETCH_ASSOC)) {
// Your code here
}
}
}
...
?>
Related
So I have look at so many post, web sites and video and now I am so confused! I can't seem to get it right.
How do you stop injection in this PHP/PDO. I have this code that works, but it allows injection.
//*THIS WORKS BUT ALLOWS INJECTION
//*
//The variable $word comes from another php file where the search is created.
public function getAllCards($word) {
$sql = "SELECT * FROM carddbtable WHERE businessNameDB='".$word."'";
foreach ($this->conn->query($sql) as $row) {
echo json_encode($row)."<br>"."<br>";
}
$db = null;
}
With this new code I am trying to remove the variable "$word" from the "SELECT * FROM " statement
to stop the injection and add the "prepare" and the error checking and the "execute" statement, but I can't get it right. How would I do this? FYI this is a GoDaddy shared server.
//Getting the search "word" from the GetCards.php
public function getAllCards($word) {
//Empty var to store all returned info from db
$returnArray = array();
// sql statement to be executed
$sql = "SELECT * FROM carddbtable WHERE businessNameDB=':word";
// prepare to be executed
$statement = $this->conn->prepare($sql);
// error occurred
if (!$statement) {
throw new Exception($statement->error);
}
// execute statement
$statement->execute( :word => '$word' );
//run the query
foreach ($this->conn->query($statement) as $row) {
echo json_encode($row)."<br>"."<br>";
}
// store all appended $rows in $returnArray to be sent to app
$returnArray[] = $row;
}
You've almost got it. PDO, like many database drivers, will be responsible for all of the escaping, so just leave the placeholder as plain as possible:
$sql = "SELECT * FROM carddbtable WHERE businessNameDB=:word";
No ' necessary there.
Now when you execute() a PDO statement you get a result which you need to capture into a variable:
$res = $statement->execute([ 'word' => $word ]);
As Ibu and chris85 point out the '$word' part is also incorrect. Avoid quoting single variables, it's not only pointless, it can cause trouble, like here where you're binding to literally dollar-sign word, not the value in question. This goes doubly for "$word".
Then you fetch from that. Right now you're calling query() on the statement, which is incorrect.
Another thing to note is kicking the habit of making throw-away variables like $sql as these are just junk. Instead pass the argument directly:
$statement = $this->conn->prepare("SELECT * FROM carddbtable WHERE businessNameDB=:word");
This avoids accidentally mixing up $sql3 with $sql8 if you're juggling a bunch of these things.
This is what i have now.
//Getting the search "word" from the GetCards.php
public function getAllCards($word) {
//Empty var to store all returned info from db
$returnArray = array();
// prepare to be executed sql statement to be executed if not entered word
$statement = $this->conn->prepare("SELECT * FROM carddbtable WHERE businessNameDB=:word");
// error occurred
// if (!$statement) {
// throw new Exception($statement->error);
// }
// execute statement
$res = $statement->execute([ 'word' => $word ]);
//run the query
foreach ($this->conn->query($res) as $row) {
echo json_encode($row)."<br>"."<br>";
}
// store all appended $rows in $returnArray to be sent to app
$returnArray[] = $row;
}
I got this working
//*FUNCTION TO GET CARD FROM SEARCH WORD CALLED FROM GetCards.php
public function getAllCards($word) {
//Connect to db using the PDO not PHP
$db = new PDO('mysql:host=localhost;dbname=xxxx', 'xxxx', 'xxxx');
//Here we prepare the SELECT statement from the search word place holder :word
$sql = $db->prepare('SELECT * FROM carddbtable WHERE businessNameDB=:word');
//We execute the $sql with the search word variable"$word"
$sql->execute([':word' => $word]);
//Looping through the results
foreach ($sql as $row)
//Print to screen
echo json_encode($row). "<br>"."<br>";
}
when trying to build robust database code (table locking, transactions, etc) i am always annoyed by the mass of code that needs to be done.
For example a transaction out of two prepared statements where i want to delete a user and update something about him in an "actions" table:
Lock Table users, actions
Start a transaction (autocommit false)
Make a prepared statement for the deletion of a user
Check if statement is != false (cause it could have already failed at 3.
Bind param
Check errorState != "00000" on the statement (can also have failed at binding params)
execute statement
Check errorState != "00000" on the statement (can also have failed at executing)
get Result of statement
Close statement
Make a new prepared statement for update actions
Check if statement != false
bind params
check statement's errorState
execute
check statement's errorState
get result
close statement
check overall transaction state, if valid commit, if not valid rollback
unlock tables
set autocommit back to true
This is how i do it (maybe im doing it wrong?). And if i do it that way its a lot of work and annoying. So i thought automateing that stuff.
What i want is something like this:
$DB->startTransaction();
$DB->query($query);
$DB->query($query2);
$DB->query($query3);
$DB->endTransaction();
And internally the database abstraction layer ontop of mysqli will take care of table locking, prepared statements and transactions itself. Shouldn't we be able to automate this?
This is one of my attempts:
public function query($query, $table, $params = null) {
if($params == null) {
$this->connection->query("LOCK TABLES $table WRITE");
$query = str_replace("!", $table, $query);
$result = $this->connection->query($query);
$this->connection->query("UNLOCK TABLES");
return $result;
}
else {
if (!$this->checkParams($query, $params)) {
return false;
}
$this->connection->query("LOCK TABLES $table WRITE");
$query = str_replace("!", $table, $query);
$stmt = $this->connection->prepare($query);
if ($stmt != false) {
$typesString = "";
foreach ($params as $param) {
if (is_numeric($param)) {
$typesString .= "i";
} else if (is_double($param)) {
$typesString .= "d";
} else {
$typesString .= "s";
}
}
$finalParamArray = array($typesString);
$finalParamArray = array_merge($finalParamArray, $params);
call_user_func_array(array($stmt, "bind_param"), $this->ref($finalParamArray));
$this->checkStatement($stmt);
$stmt->execute();
$this->checkStatement($stmt);
$result = $stmt->get_result();
$stmt->close();
$this->connection->query("UNLOCK TABLES");
return $result;
}
$this->query("UNLOCK TABLES");
return false;
}
}
This would be callable like this:
$DB->query("DELETE FROM ! WHERE userID =?", "Users", array($userID));
I am however not feeling confident about this. I googled a bit and didn't find something like i want. So my question now is: Is something like i want actually possible (well it should be)? Am i doing it wrong?
EDIT:
I also have 2 other attempts of doing this, which look MUCH MORE complicated (300+ lines of code). I can post them as well, if you want. I am still however not satisfied with them and not confident if this is actually correct!
You are right there should be an easier way of doing this, and you are also correct to say that we need an abstraction layer on top of mysqli. It is not designed to be used on its own.
You do not need so many steps. In particular, you do not need to check the return code of each method. That should already eliminate 6 or more of your steps. You do not need to close a statement either.
There's no need to specify the type when binding. Just use string type all the time. Other types come in handy very rarely, almost never.
Some time ago I posted an example of what an abstraction layer on top of mysqli could look like.
class DBClass extends mysqli {
public function __construct(
$host = null,
$username = null,
$passwd = null,
$dbname = null,
$port = null,
$socket = null
) {
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
parent::__construct($host, $username, $passwd, $dbname, $port, $socket);
$this->set_charset('utf8mb4');
}
public function safeQuery(string $sql, array $params = []): ?array {
$stmt = $this->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
}
This is far from perfect, but it shows the main idea. You can wrap a prepared statement in one single method. Simple prepare/bind/execute/get_result. Nothing more. It works with and without parameters.
In the constructor the 3 mandatory steps to opening a connection: switching error reporting, creating instance of mysqli and setting the correct charset.
If you want transactions, then you can use mysqli's begin_transaction() and commit(). They are simple enough and do not require abstraction.
I do not know why you feel you need to lock tables, but again this is a simple SQL statement and doesn't need to be abstracted.
$db = new DBClass('localhost', 'user', 'pass', 'test');
$db->safeQuery('LOCK TABLES users WRITE');
$db->begin_transaction();
$db->safeQuery('DELETE FROM users WHERE userID =?', [$userID]);
$db->safeQuery('DELETE FROM otherTable WHERE userID =?', [$userID2]);
$db->commit();
$db->safeQuery('UNLOCK TABLES');
I am trying to write a function that is supposed to receive any MySQL statement and apply it,
The basic idea is not to repeat needed code to write to Database, well what is needed to connect to Database is creating new PDO object, starting a transaction and preparing a statement, binding values to it, executing it,
so every time I want to access the Database I don't have to repeat these steps,
Here is a function that does that :
==============================================================================================
protected function applyQuery($statement, $bindparameters , &$values , $selectStatement, &$result){
try{
$dbh = DataBase::setConnection();// new PDO("MySQL= .....");
$dbh->beginTransaction();
$stmt = $dbh->prepare($statement);
if($bindparameters == true){
foreach($values as $key => $value){
$stmt->bindValue($key, $value);
}
}
$stmt->execute();
$dbh->commit();
if($selectStatement == TRUE){
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}catch (PDOException $e){
$dbh->rollBack();
throw DataBase::$Errors[0];
}
}
============================================================================================
$statement = the desired statement (e.g 'SELECT * from users WHERE username = :username')
$bindparameters = do we need to bind values (in this examples yes) so its value TRUE
&$values = array by reference in this case equals = (':username' => 'User');
$selectStatement = tells if using SELECT in statement ,in this case TRUE
$result = array by reference in this case the final fetch result will be stored in it
so in this example we get the following call to the function :
applyQuery('SELECT * from users WHERE username = :username', TRUE ,
array(':username' => 'User') , TRUE , result )
My question is : will this code work ? is the logical sequence of what it does and should do make sense ? whats the difference between $stmt->execute and $dbh->commit ? is omitting any line will cause failure to achieve the desired result
Please understand that I did lookup what is PDO and read a lot but unable to answer these questions!
I am trying to write a function that is supposed to receive any MySQL statement and apply it,
The basic idea is not to repeat needed code to write to Database, well what is needed to connect to Database is creating new PDO object, starting a transaction and preparing a statement, binding values to it, executing it,
so every time I want to access the Database I don't have to repeat these steps,
Here is a function that does that :
==============================================================================================
protected function applyQuery($statement, $bindparameters , &$values , $selectStatement, &$result){
try{
$dbh = DataBase::setConnection();// new PDO("MySQL= .....");
$dbh->beginTransaction();
$stmt = $dbh->prepare($statement);
if($bindparameters == true){
foreach($values as $key => $value){
$stmt->bindValue($key, $value);
}
}
$stmt->execute();
$dbh->commit();
if($selectStatement == TRUE){
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}catch (PDOException $e){
$dbh->rollBack();
throw DataBase::$Errors[0];
}
}
============================================================================================
$statement = the desired statement (e.g 'SELECT * from users WHERE username = :username')///
$bindparameters = do we need to bind values (in this examples yes) so its value TRUE///
&$values = array by reference in this case equals = (':username' => 'User');///
$selectStatement = tells if using SELECT in statement ,in this case TRUE///
$result = array by reference in this case the final fetch result will be stored in it///
so in this example we get the following call to the function :
applyQuery('SELECT * from users WHERE username = :username', TRUE ,array(':username' => 'User') , TRUE , result )
My question is : will this code work ?
is the logical sequence of what it does and should do make sense ?
whats the difference between $stmt->execute and $dbh->commit ?
is omitting any line will cause failure to achieve the desired result
Please understand that I did lookup what is PDO and read a lot but unable to answer these questions!
What's the best way to verify mysql executed successfully and then returned a result when you CANNOT use the following code:
$db = dbConnect();
//begin prepared statements to search db
$stmt = $db->prepare("SELECT email,authentication,email_confirm,externalid,password,id, admin FROM users WHERE email=?");
$stmt->bind_param('s',$email);
$stmt->execute();
$result = $stmt->get_result();
if (!$result){
//error statement
} if (!(mysqli_num_rows($result)==0)){
//action to perform
} else {
// no result returned
}
I was using get_result numerous times in my scripts, and my hosting provider doesn't have mysqlnd driver so I have to rewrite a lot of code. I know I am limited to bind_result and fetch(), but I need a little help rewriting the code since my mindset is stuck in the way I first did it.
I'm also using mysqli and not PDO.
The Mysqli fetch() function will return one of 3 values:
TRUE - Success. Data has been fetched
FALSE - Error occurred
NULL - No more rows/data exists or data truncation occurred
This means you can set your query like this:
$db = dbConnect();
$query = "SELECT email,authentication,email_confirm,externalid,password,id, admin FROM users WHERE email=?";
$stmt = $db->prepare();
$stmt->bind_param('s',$email);
$stmt->execute();
$stmt->bind_result($email,$auth,$email_confirm,$externalid,$password,$id,$admin);
// Will only execute loop if returns true
$record_count = 0;
while($result = $stmt->fetch())
{
// Increment counter
$record_count++;
// Do something with bound result variables
echo("Email is $email");
}
// After the loop we either ran out of results or had an error
if($result === FALSE)
{
echo("An error occurred: " . $db->error());
}
elseif($record_count == 0)
{
echo("No records exist.");
}