Right now i have a site that i need to secure from basic SQL injection attacks. The site is very basic, just a form for login and a product search page. Right now i have a file where i am keeping all of my functions used on the website. Here is a couple examples of what they look like:
function createUser($userName,$userPass){
$query = <<<STR
INSERT INTO Users (userName,userPass,userTypeID)
VALUES ('$userName','$userPass',2)
STR;
return executeQuery($query);
}
Or
function getProductByName($productName){
$query = <<<STR
SELECT productID,productName, productPic,productDesc
FROM Products
WHERE productName LIKE '%$productName%'
STR;
return executeQuery($query);
}
I want to change these so that they make use of prepared statements but am having trouble understanding how i can convert them. I found examples that make use of bindParam such as this one:
$stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)
VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd)
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();
Can i incorporate bindparam into my functions somehow? Any leads would be great.
To convert them, just do the prepare() and bindParam() parts in your shown functions and the execute() in the executeQuery() function like this
function createUser($userName,$userPass,$dbh) {
$stmt = $dbh->prepare("INSERT INTO Users (userName, userPass, userTypeID) VALUES (:name, :pass, :id)");
$stmt->bindParam(':name', $userName);
//etc
return executeQuery($stmt);
}
function executeQuery($stmt) {
$stmt->execute();
}
If you don't want to inject the $dbh, you can use global $dbh; or similar methods in the function (and don't have to change your existing code base).
But of course this will be very repetitive, as you need to write multiple bindParam() statements. The simpler way, assuming you are using PDO, would be to pass an array to execute() (as shown in the manual)
function createUser($userName,$userPass,$dbh) {
$stmt = $dbh->prepare("INSERT INTO Users (userName, userPass, userTypeID) VALUES (:name, :pass, :id)");
$params = array(
':name' => $userName,
//etc
);
return executeQuery($stmt, $params);
}
function executeQuery($stmt, $params) {
$stmt->execute($params);
}
To elaborate a bit: The real power of bindParam() comes when you a) want to use the $data_type or $length parameters (What are those?) or b) when you are using multiple execute() statements, eg a bulk insert
$stmt = $dbh->prepare("INSERT INTO users (name) VALUES (:name)");
$stmt->bindParam(":name", $name);
for (...) {
$name = ...
$stmt->execute();
}
Related
I have some method to insert some data into a database like this:
public function register($username, $email, $hashedPassword, $activationCode)
{
try {
$conn = Database::getConnection();
// Connect and create the PDO object
$conn->exec('SET CHARACTER SET utf8'); // Sets encoding UTF-8
// Define and prepare an INSERT statement
$sql = 'INSERT INTO users (username, email, pass, reset_token, dateAdded )
VALUES (:username, :pass, :email, :token, now())';
$sqlprep = $conn->prepare($sql);
// Adds value with bindParam
$sqlprep->bindParam(':username', $username, PDO::PARAM_STR);
$sqlprep->bindParam(':email', $email, PDO::PARAM_STR);
$sqlprep->bindParam(':pass', $hashedPassword);
$sqlprep->bindParam(':token', $activationCode);
// If the query is successfully executed, output the value of the last insert id
if ($sqlprep->execute()) {
//echo 'Succesfully added the row with id='. $conn->lastInsertId();
$this->result = true;
}
$conn = null; // Disconnect
} catch (PDOException $e) {
include('../views/error.php');
include('../views/admin/includes/footer.php');
exit();
}
}
The problem is I think it's not a good method if I have so many arguments for my function to enter into a database. So is it any good way I can enter a lot of fields just by using 1 parameter but still using bindParam? Since I see a lot of examples is only using prepare without bindParam. I think I can use an array, but I don't know the proper way to do it. So I need some help how I can do it.
since you want keep your bindparam i suggest you use input like this:
$input = array('username' => $username, 'activationHash' => $activationHash);
and in your bindParam add a code like this:
public function register($input){
//code
$sqlprep->bindParam(':username', $input['username'], PDO::PARAM_STR);
//other
}
hope this will solve your problem
https://stackoverflow.com/a/10060755/1747411
Check second example, you have to repeat values with binds
e.g
VALUES (:username1, :pass1, :email1, :token1, now()), (:username2, :pass2, :email2, :token2, now())
and bindParam with loop
You can insert the params as an array into $sqlprep->execute($param_array)
Or, simply passing each param into an array inside execute, like this: $sqlprep->execute(array($param1, $param2))
Update:
Pass values into $input as an array:
$input = array('username' => $username, 'activationHash' => $activationHash); //and so on
Now on the model side,
You can bind these values to params using foreach loop like this:
foreach ($values as $key => $value) {
$sqlprep->bindParam(':' . $key, $value , PDO::PARAM_STR);
}
I'm having a slight problem with the PHP PDO library and prepared statements. As far as I can see the prepared statement below should work but it doesn't, instead I get: "PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens".
My PHP code for this section looks like:
$sql = 'INSERT INTO '.POLYGON_TABLE.' (user_id, polygon, polygon_type) VALUES (:userId, PolygonFromText(\'POLYGON((:polygonArea))\'), :polygonType)';
$sth = $this->pdo->prepare($sql);
$sth->bindValue(':userId', $polygon->getUserId(), \PDO::PARAM_INT);
$sth->bindValue(':polygonArea', $polygon->getPolygonAsText(), \PDO::PARAM_STR);
$sth->bindValue(':polygonType', $polygon->getPolygonType(), \PDO::PARAM_STR);
if($sth->execute()) {
return true;
} else {
return false;
}
I have done a var_dump of $polygon->getUserId(), $polygon->getPolygonAsText() and $polygon->getPolygonType() and get the following:
string(1) "1"
string(226) "53.897910476098765 -1.739655277929728, 53.865530797116 -2.080231449804728, 53.67235280490181 -2.006073734960978, 53.68862047002787 -1.621552250585978, 53.89305512284903 -1.539154789648478, 53.897910476098765 -1.739655277929728"
string(7) "commute"
The issue is with $polygon->getPolygonAsText() as commenting out this particular bindValue call and the PolygonFromText(\'POLYGON((:polygonArea))\') from the SQL statement causes the query to work.
I'm now completely at a loss. Anyone know what's wrong here? I can't see anything wrong with the text contained within $polygon->getPolygonAsText(). I have searched high and low for a solution to this and spent several hours this evening tinkering with the code but to no avail.
I have even tried the suggestions in these 2 stack overflow topics but they didn't work either:
Invalid parameter number on PDO Prepared Statement
PHP PDO prepared statements
Any help would be much appreciated...
Did you try passing in the entire expression as the bind value?
$sql = 'INSERT INTO '.POLYGON_TABLE.' (user_id, polygon, polygon_type) VALUES (:userId, PolygonFromText(:polygonArea), :polygonType)';
$sth = $this->pdo->prepare($sql);
$area = sprintf("POLYGON((%s))", $polygon->getPolygonAsText());
$sth->bindValue(':userId', $polygon->getUserId(), \PDO::PARAM_INT);
$sth->bindValue(':polygonArea', $area, \PDO::PARAM_STR);
$sth->bindValue(':polygonType', $polygon->getPolygonType(), \PDO::PARAM_STR);
It appears that you're trying to use a named parameter inside a string:
PolygonFromText(\'POLYGON((:polygonArea))\')
This would be akin to doing something like this:
UPDATE foo SET bar = 'blah blah :wontwork blah blah'
What you should try instead is binding the whole string in the query:
PolygonFromText(:polygonArea)
And then including the rest of the string in the bound value:
$sth->bindValue(':polygonArea', 'POLYGON((' . $polygon->getPolygonAsText() . '))', \PDO::PARAM_STR);
Last resort you could do this:
$sql = "INSERT INTO ".POLYGON_TABLE." (user_id, polygon, polygon_type) "
."VALUES (:userId, PolygonFromText('POLYGON(". $polygon->$getPolygonAsText
.")'),:polygonType)";
But I think you should try the ? params first and see how that goes.
$sql = "INSERT INTO ".POLYGON_TABLE." (user_id, polygon, polygon_type) "
."VALUES (?, PolygonFromText('POLYGON(?)'), ?);";
$data = array($polygon->getUserId(), $polygon->getPolygonAsText(), $polygon->getPolygonType());
$query->execute($data);
Btw, I also think those single quotes around the POLYGON(?) function are dodgy... usually you don't quote a method call do you?
As I am writing a script, I am typing $db->prepare()'s and $stmt->bindParam()'s constantly. I am looking for a way to consolidate it all in a function. This is what I have so far.
$sql = "SELECT (name, email) FROM users WHERE VALUES (:name, :email)"
$values = array(':name' => 'my_name', ':email' => 'blahblah#example.com', );
function db_query($sql, $values) {
global $db; //Database object
$stmt = $db->prepare($sql);
foreach($values as $placeholder => $value) {
$stmt->bindParam($placeholder, $value);
}
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
Would this be sufficient for most queries? Is this a secure way to do this?
I just want the query to run and return whatever it returns (NULL, values, error, etc...).
Thanks.
Your code will not work as expected because bindParam() binds the placeholder name (first argument) to the variable reference present in the second argument.
Using your example, this would result in all parameters set to blahblah#example.com as it is the last $value in the loop.
As mentioned in the comments, simply use $stmt->execute($values). See http://php.net/manual/en/pdostatement.execute.php
If you really want to continue with your loop, use PDOStatement::bindValue() instead.
Is there a way I can put these bindParam statements into one statement?
$q = $dbc -> prepare("INSERT INTO accounts (username, email, password) VALUES (:username, :email, :password)");
$q -> bindParam(':username', $_POST['username']);
$q -> bindParam(':email', $_POST['email']);
$q -> bindParam(':password', $_POST['password']);
$q -> execute();
I was using mysqli prepared before where it was possible, I switched to PDO for assoc_array support. On the php.net website for PDO it shows them on seperate lines, and in all examples I have seen it is on seperate lines.
Is it possible?
Example 2 on the execute page is what you want:
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
You may want to look at the other examples too. With question mark parameters, it would be:
$q = $dbc -> prepare("INSERT INTO accounts (username, email, password) VALUES (?, ?, ?)");
$q->execute(array($_POST['username'], $_POST['email'], $_POST['password']));
If those are the only columns, you can just write:
$q = $dbc -> prepare("INSERT INTO accounts VALUES (?, ?, ?)");
$q->execute(array($_POST['username'], $_POST['email'], $_POST['password']));
helper function is a function that makes you help to avoid writing bunch of repetitive code every time you want to run a query.
This is called "programming" and there is almost none of it on this site, at least under "PHP" tag.
While many peiople thinks that programming stands for copy/pasting chunks of code from manual examples, it's somewhat different.
Although it's hard to learn but really worth it, especially if you're devoting yourself to web-developing.
As you can see, no accepted answer did no real help for you, as you still have to write something like
$sth->execute(array(':username' => $_POST['username'],
':email' => $_POST['email']
':password' => $_POST['password']);
as many times as many fields in your table, which makes not much difference from your initial approach, still makes you write each field name FOUR times.
But being a programmer, you can use powers of programming. A loop, for example - one of cornerstone programming operators.
Every time you see repetitions, you know there should be a loop.
for example, you can set up a list of fields, naming them only once.
And let a program do the rest.
Say, such a function like this one
function pdoSet($fields, &$values, $source = array()) {
$set = '';
$values = array();
if (!$source) $source = &$_POST;
foreach ($fields as $field) {
if (isset($source[$field])) {
$set.="`$field`=:$field, ";
$values[$field] = $source[$field];
}
}
return substr($set, 0, -2);
}
being given an array of field names, it can produce both insert statement and data array for you. Programmatically. So, your code become no more than these 3 short lines:
$fields = array('username', 'email', 'password');
$stmt = $dbh->prepare("INSERT INTO accounts SET ".pdoSet($fields,$values));
$stmt->execute($values);
Your Common Sense is totally right that the aim of coding is to save typing... but his solution doesn't help with the BindParams bit. I couldn't find anything else about this online, so here's something I finally just persuaded to work - I hope it's useful for someone!
//First, a function to add the colon for each field value.
function PrepareString($array){
//takes array (title,author);
//and returns the middle bit of pdo update query :title,:author etc
foreach($array as $k =>$v){
$array[$k]=':'.$v;
}
return implode(', ', $array);
}
Then...
function PdoInsert($table_name,$array){
$db = new PDO(); //however you create your own pdo
//get $fields and $vals for statement
$fields_vals=array_keys($array);
$fields=implode(',',$fields_vals);
$vals=PrepareString($fields_vals);
$sql = "INSERT INTO $table_name($fields) VALUES ($vals)";
$qwe=$db->prepare($sql);
foreach ($array as $k =>$v ){
//add the colon to the key
$y=':'.$k;
//god knows why it doesn't like $qwe->bindParam($y,$v,PDO::PARAM_STR);
// but it really doesn't! So we refer back to $array.
//add checks for different binding types here
(see PDO::PARAM_INT is important in bindParam?)
$qwe->bindParam($y,$array[$k],PDO::PARAM_STR);
}
if ($qwe->execute()==true){
return $db->lastInsertId();
}
else {
return $db->errorCode();
}
}
Then you can insert anything by doing
PdoInsert('MyTableName',array('field1'=>$value1,'field2'=>$value2...));
Having previously sanitized your values of course.
+1 to Matthew Flaschen for the accepted answer, but I'll show you another tip. If you use SQL parameters with names the same as the entries in $_POST, you could take advantage of the fact that $_POST is already an array:
$q->execute($_POST);
The SQL parameter names are prefixed with a colon (:) but the keys in the $_POST array are not. But modern versions of PDO account for this - you no longer need to use colon prefixes in the keys in the array you pass to execute().
But you should be careful that anyone can add extra parameters to any web request, and you should get only the subset of $_POST params that match parameters in your query.
$q = $dbc -> prepare("INSERT INTO accounts (username, email, password)
VALUES (:username, :email, :password)");
$params = array_intersect_key($_POST, array("username"=>1,"email"=>1,"password"=>1));
$q->execute($params);
Personally, I prefer to use a wrapper function for all of pdo, which simplifies the code necessary substantially.
For example, to run bound queries (well, all my queries), I do this:
$iterable_resultset = query("INSERT INTO accounts (username, email, password) VALUES (:username, :email, :password)", array(':username'=>'bob', ':email'=>'bob#example.com', ':password'=>'bobpassword'));
Note that not only is the sql simply a string, but it's actually a reusable string, as you can simply pass the sql as a string and change the array of variables to pass in if you want to perform a similar insert right after that one (not applicable to this situation, but applicable to other sql use cases).
The code that I use to create this wrapper function is as below:
/**
* Run bound queries on the database.
*
* Use: query('select all from players limit :count', array('count'=>10));
* Or: query('select all from players limit :count', array('count'=>array(10, PDO::PARAM_INT)));
*
* Note that it returns foreachable resultset object unless an array is specifically requested.
**/
function query($sql, $bindings=array(), $return_resultset=true) {
DatabaseConnection::getInstance(); // Gets a singleton database connection
$statement = DatabaseConnection::$pdo->prepare($sql); // Get your pdo instance, in this case I use a static singleton instance. You may want to do something simpler.
foreach ($bindings as $binding => $value) {
if (is_array($value)) {
$first = reset($value);
$last = end($value);
// Cast the bindings when something to cast to was sent in.
$statement->bindParam($binding, $first, $last);
} else {
$statement->bindValue($binding, $value);
}
}
$statement->execute();
if ($return_resultset) {
return $statement; // Returns a foreachable resultset
} else {
// Otherwise returns all the data an associative array.
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
}
// Wrapper to explicitly & simply get a multi-dimensional array.
function query_array($sql_query, $bindings=array()) {
return query($sql_query, $bindings, false); // Set return_resultset to false to return the array.
}
As noted in the comments, you'd want to use your own method for setting up a database connection and getting an initialized pdo, but in general it allows your bound sql to be cut down to just a single line.
Thanks for looking. All helpful answers/comments are up voted.
In php, you can use NOW() like this:
mysql_query("INSERT INTO tablename (id, value, time_created)
VALUES ('{$id}', '{$value}', NOW())");
How can I do the same thing in PDO. When I bind like this, I get an error:
$stmt->bindParam(':time_added', NOW(), PDO::PARAM_STR);
Is it the PDO:PARAM_STR?
Because nobody has explicitly answered the question, I'll add the correct answer for the sake of completeness.
$stmt = $pdoDb->prepare('INSERT INTO tablename (id, value, time_created) VALUES (:id, :value, NOW())');
// either bind each parameter explicitly
$stmt->bindParam(':id', $id); // PDOStatement::bindValue() is also possibly
$stmt->bindParam(':value', $value);
$stmt->execute();
// or bind when executing the statement
$stmt->execute(array(
':id' => $id,
':value' => $value
));
Presuming your PDO statement is correct you could do something like this:
$date = date('Y-m-d H:i:s');
$stmt->bindParam(':time_added', $date, PDO::PARAM_STR);
None of the answers solve the question as I see it!
So there are some of my findings:
there is NO WAY how to force PDO to pass MySQL function call as a query value - so there is no way to do simple wrapper that will be able to use NOW() or any other function as passed values. Every time you need something like that, you need manually change the query, so the function call is part of the query string. :-(
I'm using function that tests given values for MySQL function I am using and modifies the query itself, but it is not a good solution to my opinion... :-}
This might be useful to some of you, maybe not. I was confronted with the same problem as Ollie Saunders was. I'm pretty new to php/mysql, and most of all PDO. I was able to solve the problem with the following:
$active = 0;
$id = NULL;
$query = "INSERT
INTO tbl_user(ID_user, firstname, lastname, email, password, active, create_date)
VALUES (?,?,?,?,?,?,NOW())";
if($stmt=$this->conn->prepare($query)) {
$stmt->bind_param('issssi', $id, $firstname, $lastname, $email, $password, $active);
$stmt->execute();
}
and guess what it works! Hope to have helped here. Any comments are welcome. Try it and tell me if it worked for you, or if you have any additions.
To answer Elmo's question, you can create a PDO wrapper that allows for SQL functions like NOW(). You just need to pass an additional argument with the columns that you want to use SQL functions for. Here's mine:
function pInsertFunc($action, $table, $values, $sqlfunctions)
{
global $pdb;
// There's no way to pass an SQL function like "NOW()" as a PDO parameter,
// so this function builds the query string with those functions. $values
// and $sqlfunctions should be key => value arrays, with column names
// as keys. The $values values will be passed in as parameters, and the
// $sqlfunction values will be made part of the query string.
$value_columns = array_keys($values);
$sqlfunc_columns = array_keys($sqlfunctions);
$columns = array_merge($value_columns, $sqlfunc_columns);
// Only $values become ':paramname' PDO parameters.
$value_parameters = array_map(function($col) {return (':' . $col);}, $value_columns);
// SQL functions go straight in as strings.
$sqlfunc_parameters = array_values($sqlfunctions);
$parameters = array_merge($value_parameters, $sqlfunc_parameters);
$column_list = join(', ', $columns);
$parameter_list = join(', ', $parameters);
$query = "$action $table ($column_list) VALUES ($parameter_list)";
$stmt = $pdb->prepare($query);
$stmt->execute($values);
}
Use it like this:
$values = array(
'ID' => NULL,
'name' => $username,
'address' => $address,
);
$sqlfuncs = array(
'date' => 'NOW()',
);
pInsertFunc("INSERT INTO", "addresses", $values, $sqlfuncs);
The query string that results looks like this:
INSERT INTO addresses (ID, name, address, date) VALUES (:ID, :name, :address, NOW())
other than NOW() i also utilize the "timestamp" type column and set its default to CURRENT_TIMESTAMP .. so i just pass nothing for that field and time is automatically set. maybe not exactly what ur looking for.