bindValue and bindParam in mysqli and PDO ignore variable type - pdo

I'm having problems in understanding a part of the meaning of binding certain variable types in PDO and mysqli if the type given, in my case, seems to be meaningless. In the following code, the type bound (like i or s) gets ignored. The table row "wert_sortierung" in the database is INT(11). Regardingless if $val_int is really integer or not and if I bind it via i,s / PDO::PARAM_INT or _STR, the query always works, no break, no error or warning, that the types in the binding and database or variable itself don't fit.
<?
class PDOTest {
protected $pdo;
function __construct(){
$usr="usr";
$pwd="pwd";
$host="localhost";
$db="db";
$val_int="I'm a string";
$val_str="OP";
$querystring="SELECT wert_langtext FROM TB_wert WHERE wert_sortierung = ? AND wert_CD = ?";
try {
$db_info = "mysql:host=$host;dbname=$db"; // usually provided via require_once and during construction
$this->pdo = new PDO($db_info, $usr, $pwd);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $this->pdo->prepare($querystring);
$stmt->bindValue(1,$val_int,PDO::PARAM_INT);
$stmt->bindValue(2,$val_str,PDO::PARAM_STR);
$stmt->execute();
$row_return = $stmt->fetchAll(PDO::FETCH_ASSOC);
$this->varprint($row_return);
$this->pdo = NULL;
}
catch (PDOException $ex) {
printf ('Es spricht:');
$this->printerror("Fehla! (" . $ex->getMessage() . ")");
$this->pdo = NULL;
exit();
}
printf("<br />-------<br />");
//Added for comparison
$mysqli = new mysqli($host, $usr, $pwd, $db);
$m_stmt = $mysqli->prepare($querystring);
$m_stmt->bind_param('is',$val_int, $val_str);
$m_stmt->execute();
$m_stmt->bind_result($row_return);
$m_stmt->fetch();
$this->varprint($row_return);
$m_stmt->close();
$mysqli->close();
}
private function printerror($txt) {
printf("<p><font color=\"#ff0000\">%s</font></p>\n",
htmlentities($txt));
}
private function varprint($var) {
echo "<br />";
echo "<pre>";
echo var_dump($var);
echo "</pre>";
}
}
new PDOTest();
?>
Please can anyone point out my error in reasoning.

It is actually Mysql's loose-typing that that deceived you.
As a matter of fact, regular Mysql queries can accept strings for the numberic values all right:
SELECT wert_langtext FROM TB_wert WHERE wert_sortierung = '1' AND wert_CD = '1';
while prepared statement just following this behavior.
However, a contrary situation is not that harmless. Addressing a string value with a number
SELECT wert_langtext FROM TB_wert WHERE wert_sortierung = 1;
will cause infinite number of warnings in case of wert_sortierung being of string type and some unexpected behavior, like matching for ALL the rows.
So, as a general advise I would suggest to always use 's' by default. The only drawback is PDO's emulated prepares and it can be easily worked around.
So, to answer your question explicitly - prepared statements just allow the same behavior as regular queries, adding nothing to it. Everything that possible with a regular query, is possible with prepared statement as well. And no, binding do not validate your data (however it should. Imn my class I test integer placeholders and throw an exception if no numeric value given)

Related

PHP 7.1.7 - A non-numeric value encountered - even though already checked is_numeric

I've getting this non-numeric error when trying to add even though I've already checked with is_numeric. I've tried converting the $value with number_format, (float), (int). Nothing seems to work. The values are present and number from a posted multiple field.
$amount = 0;
$transaction_amount = isset($_POST['TransactionCharge']['amount']) ? $_POST['TransactionCharge']['amount']: array();
foreach($transaction_amount as $value) {
if ( is_numeric($value) ) {
$amount += number_format($value,2);
}
}
Interesting note:
$amount = array_sum($transaction_amount);
seems to work in lieu of iterating array. Is that the only way to do this in 7.1?
Simply used array_sum method to implement.

PDO_OCI Semicolon

It seem that when SQL query has semicolon at the end, prepare/execute statements fail. I get ORA-00911: invalid character error. What I would like to know if this is a pdo_oci driver bug? Has anyone encountered this issue before?
Example:
try{
$conn = new PDO($dsn,$db_username,$db_password,$options);
}catch(PDOException $e)
{
echo ($e->getMessage());
}
try{
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sqlQuery = 'SELECT "object" FROM "statements" WHERE "subject" = ? and "predicate" = ?;';
$sth = $conn->prepare($sqlQuery);
$sth->execute(array('http://192.168.1.234/taooracle/tao_ora_dev.rdf#i1386330868934416', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'));
while ($row = $sth->fetch()){
$uri = $row['object'];
echo $uri;
}
}catch(PDOException $e){
print $e->getMessage() ."<br />";
print $e->getLine() ."<br />";
}
SQLSTATE[HY000]: General error: 911 OCIStmtExecute: ORA-00911: invalid character
It's easy enough to remove the semicolon, but there are more queries like that, and I want if there's a better option for this.
This is not a bug, this is working as it should. The ending semicolon is not a part of SQL, it's just a marker for programs like sqlplus, mysql or whatever that one statement is finished. So, you shouldn't be using these semicolons in your code.
I know, mysql accepts them, probably because the devs just put that in after answering too many "why doesn't that work?" questions. But the correct way is to remove the semicolons from your code - the fact that mysql accepts them is more of a bug than the fact other databases don't :-)

Invalid parameter number: number of bound variables does not match number of tokens PDO insert

function mysql_insert($data_array){
$sql = "insert into `". $this->table_name. '`';
$array_keys = array_keys($data_array);
$array_keys_comma = implode(",\n", preg_replace('/^(.*?)$/', "`$1`", $array_keys));
for($a=0,$b=count($data_array); $a<$b; $a++){ $question_marks .="?,"; }
$array_values = array_values($data_array);
$array_values_comma = implode(",", $array_values);
$sql.= " ($array_keys_comma) ";
$sql.= " values(". substr($question_marks, 0,-1) .")";
$prepare = $this->connDB->prepare($sql);
$insert = $prepare->execute(array($array_values_comma));
}
I want to creat like this universal functions, $data_array-comes from $_POST
This function will work for all form. But i dont know what is my wrong :S
I don't know what is my wrong
That's quite easy to know: number of bound variables does not match number of tokens.
I want to creat like this universal functions, $data_array-comes from $_POST
Here you go: Insert/update helper function using PDO
$array_values_comma is a scalar after you implode() the array. So you always pass an array of one element to your execute() function. You should pass $array_values.
Here's how I'd write this function:
function mysql_insert($data_array){
$columns = array_keys($data_array);
$column_list_delimited = implode(",",
array_map(function ($name) { return "`$name`"; }, $columns));
$question_marks = implode(",", array_fill(1, count($data_array), "?"));
$sql = "insert into `{$this->table_name}` ($column_list_delimited)
values ($question_marks)";
// always check for these functions returning FALSE, which indicates an error
// or alternatively set the PDO attribute to use exceptions
$prepare = $this->connDB->prepare($sql);
if ($prepare === false) {
trigger_error(print_r($this->connDB->errorInfo(),true), E_USER_ERROR);
}
$insert = $prepare->execute(array_values($data_array));
if ($insert === false) {
trigger_error(print_r($prepare->errorInfo(),true), E_USER_ERROR);
}
}
A further improvement would be to do some validation of $this->table_name and the keys of $data_array so you know they match an existing table and its columns.
See my answer to escaping column name with PDO for an example of validating column names.

PDO login script won't work

I changed this login script to PDO. Now it passes the username but get's stuck fetchAll line. I need help please. thanks
<?php
session_start();
include_once"includes/config.php";
if (isset($_POST['admin_login'])) {
$admin_user = trim($_POST['admin_user']);
$admin_pw = trim($_POST['admin_pw']);
if ($admin_user == NULL OR $admin_pw == NULL) {
$final_report.="Please complete all the fields below..";
} else {
$check_user_data = $db->prepare("SELECT * FROM `admin`
WHERE `admin_user`='$admin_user'");
$check_user_data->execute();
if ($check_user_data->fetchColumn() == 0) {
$final_report.="This admin username does not exist..";
} else {
$get_user_data = $check_user_data->fetchAll($check_user_data);
if ($get_user_data['admin_pw'] == $admin_pw) {
$start_idsess = $_SESSION['admin_user'] = "".$get_user_data['admin_user']."";
$start_passsess = $_SESSION['admin_pw'] = "".$get_user_data['admin_pw']."";
$final_report.="You are about to be logged in, please wait a few moments...";
header('Location: admin.php');
}
}
}
}
?>
Not checking return value prepare() or execute() for false. You need to check for SQL errors and handle them, stopping the code instead of continuing on blithely.
Not using query parameters in the prepared statement, still interpolating $_POST content into the query unsafely. You're missing the benefit of switching to PDO, and leaving yourself vulnerable to SQL injection attack.
You're storing passwords in plaintext, which is unsafe. See You're Probably Storing Passwords Incorrectly.
Do you really need to SELECT * if you only use the admin_pw column? Hint: no.
PDOStatement::fetchAll() returns an array of arrays, not just one array for a row. Read the examples in the documentation for fetchAll().

Is this little Doctrine2 dynamic SQL enough safe of injection?

I kwnow that using an ORM like Doctrine2 for building queries is safe, meaning that parameters are escaped by default.
But i'm guessing that this is not so obvious when using literals and when this literal comes directly from the query string:
$builder = $this->getRepository()->createQueryBuilder('e');
$request = $this->getRequest();
// Loop each allowed filter field and check if exists in $request
foreach($this->getFilterFields() as $filter) :
// Skip falsy values in $request
if(!$value = $request->get($filter)) continue;
// Add OR LIKE %$value% where $value is GET paramter
$like = $builder->expr()->literal("%$value%");
$builder->orWhere($builder->expr()->like("e.$filter", $like));
endforeach;
Should safety be improved in some way?
$queryBuilder->expr returns an ExpressionBuilder object. Inside ExpressionBuilder we find:
public function literal($input, $type = null)
{
return $this->connection->quote($input, $type);
}
So literals do get quoted and should be fine to use.
We also find:
public function like($x, $y)
{
return $this->comparison($x, 'LIKE', $y);
}
public function comparison($x, $operator, $y)
{
return $x . ' ' . $operator . ' ' . $y;
}
$y is fine because it goes through literal first. Do want to be a bit careful about $x. As long as your filterFields are internal then no problem. If they are coming from the user then you need to make sure they are valid.