Prepared statement in PostgreSQL, string and number - php

php code:
$con = new PDO($this->dsn, $this->login, $this->password);
$stmt = $con->prepare($selectSql);
$stmt->execute(array(
'login' => $login
));
and sql query:
$selectSql = 'SELECT id
FROM public.users
WHERE (numberType = :login OR stringType = :login)';
The problem is that I do not find it desirable to edit PHP Code and add new variables to the example 'loginNumber' => (int) $login.
How can I fix it so that the SQL search result itself would be transferred to number value?
numberType = (:login)::Integer parameter did not work.

I have revised my answer given new comments, you will need to detect what data format has been provided. This is tricky as it seems your data always comes as a string, even if the string contains a raw integer.
The sloppy way to do this is to do a quick is_numeric check... but this will parse any numeric value as true... even floats? On-top of that, variable binding assumes string by default in PDO, to bind anything else you must verbosely define it.
Here is some loose code to give you an idea, but you may have to improve the $isNumericLogin logic for more accurate results for numeric non-integer login values.
$con = new PDO($this->dsn, $this->login, $this->password);
// Generate correct query given the data format
$isNumericLogin = is_numeric($login);
$selectSql = $isNumericLogin ?
'SELECT id FROM public.users WHERE number = :login' :
'SELECT id FROM public.users WHERE string = :login';
$stmt = $con->prepare($selectSql);
// Bind the correct data format based
$isNumericLogin ?
$stmt->bindValue('login', $login, PDO::PARAM_INT) :
$stmt->bindValue('login', $login);
$stmt->execute();

Related

Is the in_array function a safe way of blocking code injection/sql injection?

If i have a php file which is receiving a $_GET['value'] is it safe from sql injection or code-injection for me to start my php file with
if (in_array($_GET['value'], $allowed_values);)
{ /* normal page code handling the $_GET['value'] */
} else { unset($_GET['name'])
}
$allowed values is obviously an array of all values which i am expecting as safe for $_Get['value']. Is this still unsafe? Thank you.
Yes, that's a common and safe technique that can be used in situations where query parameters can't be used. For instance, if the value will be used as a table or column name, you can't provide it as a query parameter, you have to substitute it directly into the SQL string. Whitelisting like this is the recommended way to ensure that this is safe.
It depends on the values in the $allowed_values array, and how you are interpolating the value into your SQL query.
For example:
$allowed_values = [ 'a word' ];
if (in_array($_GET['value'], $allowed_values)) {
$sql = "SELECT * FROM mytable WHERE id = {$_GET['value']};";
}
Definitely not safe. It results in the SQL:
SELECT * FROM mytable WHERE id = a word;
This is a syntax error.
Why would you not just use SQL query parameters? Then you don't need to worry if it's safe or not. Query parameters separate the values from the SQL parsing, so there's no way any kind of value can cause SQL injection.
You won't have to have an $allowed_values array. You won't have to remember to check if the GET input is in the array. You won't have to worry about quoting or escaping.
It's true that query parameters only work for values, that is in place of a quoted string literal or quoted datetime literal or numeric literal. If you need other parts of your query to be dynamic, like the table name or column name or SQL keywords, etc. then use an allow-list solution like you are showing.
But the more common case of interpolating dynamic values is better handled by query parameters:
$sql = "SELECT * FROM mytable WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt-execute( [ $_GET['value'] ] );
let's discuss this thing in little details:
Your code is like this :
if (in_array($_GET['value'], $allowed_values);) {
...........
$sql = "SELECT * FROM mytable WHERE id = $_GET['value']";
...........
}
else {
unset($_GET['name'])
}
now let's assume, you have some values :
the in_array() function will allow only some pre-defined values, you couldn't have the option to take custom user input by $_GET, but as only pre-defined values are allowed,any SQL command will be safe inside if statement.
now take this example of $allowed_values array :
$allowed_values = ['some details' , 'another details' ,3, ' 105; DROP TABLE mytable;', 22 , 'ok'];
If any of these array values have a string that can have potential SQL injection capability, then there will be an issue. but I think you will not put any such string in the array $allowed_values. ( in this above-mentioned example, index 3, ' 105; DROP TABLE mytable;' can delete the table mytable ). else the SQL command will be safe.
now you can add an extra layer of safety in the code, by using PDO for any SQL query. (in this example you do not need that, as in_array() function is 100% safe unless you yourself put any malicious code in the array, as per my above-mentioned example). but for other types of user input where you have to do some SQL query depend on the user input, you can use PDO -prepared statement.
a PDO example is this :
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("INSERT INTO photos (username, kname) VALUES (?, ?)");
$stmt->execute([ $username , $kname ]);
For more info, try w3school link: https://www.w3schools.com/php/php_mysql_prepared_statements.asp

PHP/Oracle - OCI doesn't like my data type?

I'm new to OCI, but just trying to do a basic oci_bind_by_name in PHP to get it working. But even with a simple select statement and a dummy variable, it rejects the variable type.
Here's the code:
$conn = oci_connect($username, $password, $database);
$dummy = "dummy#dummy.com";
$u = oci_parse($conn, "select ca_email from pwv_google_group");
oci_bind_by_name($u, ':ca_email', $dummy);
But it just returns:
Warning: oci_bind_by_name(): ORA-01036: illegal variable name/number
Since this is a very simple query/parameter, I can only assume my syntax is wrong or something might be off on the Oracle side (ca_email really should be a varchar, but I'm using Datagrip which doesn't allow DESC command, so I don't know how to validate that). Is something else wrong?
oci_bind_by_name() is expecting you to bind a value for some form of input to the SQL statement - from the manual
bv_name The colon-prefixed bind variable placeholder used in the
statement. The colon is optional in bv_name. Oracle does not use
question marks for placeholders.
So for your example, it would be more like
$u = oci_parse($conn, "select * from pwv_google_group where ca_email = :ca_email");
oci_bind_by_name($u, ':ca_email', $dummy);
As you are trying to retrieve the values from the data you just need to fetch the data as in (hacked from example #3 in manual)
$u = oci_parse($conn, 'select ca_email from pwv_google_group');
oci_execute($u);
$row = oci_fetch_array($u, OCI_ASSOC+OCI_RETURN_NULLS);
foreach ($row as $item) {
print $item."<br>\n";
}

Changing MysQL value with PHP not with user id but username?

I am trying to make simple form where you put user_id etc. etc. and it will change in mysql, which works pretty fine but the question is, is there any way I could instead of users_ids use usernames ? thanks
if(isset($_POST['btn-change'])) {
$account = strip_tags($_POST['account']);
$value = strip_tags($_POST['value']);
$string = strip_tags($_POST['string']);
$account = $DBcon->real_escape_string($account);
$value = $DBcon->real_escape_string($value);
$string = $DBcon->real_escape_string($string);
$sql = "UPDATE tbl_users SET $value='$string' WHERE user_id=$account";
if ($DBcon->query($sql) === TRUE) {
$msg1 = '<div class="alert alert-success">
<i class="fa fa-info" aria-hidden="true"></i> Successfully changed !
</div>';
} else {
$msg1 = '<div class="alert alert-danger">
<i class="fa fa-info" aria-hidden="true"></i></span> Something went wrong !
</div>';
}
$DBcon->close();
}
The original MySQL extension has been removed in PHP7 and was deprecated in PHP 5.5.
Don't be intimidated by PDO though, it's actually super simple and much better.
Require this code at the start of any PHP files that need to make database queries.
$host = "127.0.0.1";
$port = "3306";
$dbname = "NameOfTheDatabase";
$dsn = "mysql:host=$host;dbname=$dbname;port=$port;";
$dbuser = "UsernameForDatabase";
$dbpass = "PasswordForDatabase";
$options = array(PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8");
$database = new PDO($dsn, $dbuser, $dbpass, $options);
Note that it can be improved upon drastically but for the sake of example, we'll keep things simple. Usually you would want to load this information from a protected config file.
Now let's say we want to select the user by their username.
$statement = $database->prepare("
SELECT user_id
FROM tbl_users
WHERE username LIKE :username
");
$statement->bindParam("username", $_POST['username'], PDO::PARAM_STR);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_OBJ);
echo $result->user_id;
Basically what we're doing here is telling PDO to store a query that we've prepared in the $statement variable. We then tell it to use bindParam() to replace or bind :username with the POST variable (this will take care of escaping).
After the statement is executed using execute(), we have two options. The fetch() function essentially takes the next (or in this case, first) returned row and stores it in the $result variable. After that, you can access the returned columns using their names, in this case, $result->user_id. However if you SELECTed the rank, for example, you would be able to access it with $result->rank.
The second option is the fetchAll() function which works in nearly the same way, except that it takes every row returned and stores it in an array. Even if only one row is returned. That means that you'd need to access the data using $result[0]->user_id where 0 is the returned row.
foreach($result as $row){
echo $row->user_id;
}
Or you can loop over the result array using foreach as above. One of the greatest features of PHP in my opinion.
Updating and inserting works in nearly the same way except that no rows are returned by the execute function. Instead, the amount of rows that were changed is returned.
$statement = $database->prepare("
UPDATE tbl_users
SET email = :email
WHERE user_id = :user_id
");
$statement->bindParam("email", $_POST['email'], PDO::PARAM_STR);
$statement->bindParam("user_id", $_POST['account'], PDO::PARAM_INT);
$statement->execute();
echo "Rows changed: ".$statement->rowCount();
In that example, we're just updating the user's email address using a POST variable for the email and the account variable for the user_id. Notice that I used PDO::PARAM_INT for the third parameter of bindParam, that's just because the user_id is an integer. Usually you can get away with PDO::PARAM_STR but it's supposed to be for strings.
If you want a bit more detail by somebody far better at explaining things than me, check this introduction to PDO out. Hope this gets you on the right track!

Numerical PHP variables in MySQL queries

I'm trying to build a query in a script that relies upon an object's attributes in order to retrieve the correct information. What I have is this:
$query = "SELECT fields FROM table WHERE fieldA = $this->x";
//Processing of results here
I've seen queries like this used before with string variables but I'm not sure if the rules are different if you're using a variable that you know has a numerical value and the corresponding column for said value is declared as an integer or decimal. Would I need to include single quotes around $this->x?
Use PDO to do that:
$user = "username";
$password = "password";
$pdo = new PDO('mysql:host=localhost;dbname=dbname', $user, $pass);
$stmt = $pdo->prepare('SELECT fields FROM table WHERE fieldA = :value');
$stmt->execute(array('value' => $this->x);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
Print the result:
echo '<pre>';
print_r($result);
echo '</pre>';
Or bind one value:
$sth->bindValue(':value', $this->x, PDO::PARAM_INT);
For MySQLi see:
How can I prevent SQL injection in PHP?
As you said $this->x is numeric. I make a small test. Its working fine.
class foo{
public $x;
public function showQuery (){
$this->x = 10;
return "SELECT fields FROM table WHERE fieldA = $this->x";
}
}
$ob = new foo();
echo $ob->showQuery();
OUTPUT :
SELECT fields FROM table WHERE fieldA = 10
Note : It depends upon your wish you want to use PDO or not. is it acceptable to use a standard string query and execute it with a mysqli database handler? : YES

PHP/MySQL/PDO binding null parameter doesn't work

I am having trouble binding a null parameter in the following code
$nullVariable = NULL;
$sql = new PDO('mysql:host=' . $Server, $User, $Password);
$sql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column = :uuid");
$statement->bindParam(":uuid", $nullVariable, PDO::PARAM_NULL);
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
The results variable will be a empty array. If I dont use parameters and modify my query to "WHERE Binary16Column IS NULL" it returns the expected number of rows. So the problem must be with how I am handling the parameter, rather than my SQL query.
My code is more complex than listed above, and I need to be able to use a parameter variable which may be null, so checking to see the variable is null and running a different query is less than ideal. Technically I have my own function for setting parameters, this is where I am checking if the contents of the variable is null, and binding the parameter appropriately, so I dont have to write an unnecessary number of queries. The query works also works fine if the variable contains valid data, and the parameter type is PARAM_LOB.
Does anyone know what i'm doing wrong? Thanks a lot!
Read up on three-valued logic. NULL is not a value; it is a marker for the absence of a value, and so NULL can never be equal to anything, including itself.
However, there is a null-safe comparison operator also known as the "spaceship operator," which does consider two nulls to be equivalent.
WHERE Binary16Column <=> :uuid
... should do what you expected.
If you want to select the record with Binary16Column is null, you need to use IS NULL as the condition, but not = NULL.
SELECT * FROM Table WHERE Binary16Column IS NULL
You need to do:
$uuid = /**some value**/;
$sql = new PDO('mysql:host=' . $Server, $User, $Password);
$sql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
if ($uuid === null) {
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column IS NULL");
} else {
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column = :uuid");
$statement->bindParam(":uuid", $uuid);
}
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
I think the reason you are not getting a result because NULL is a keyword. Because of the way MySQL treats NULL values, I think you are going to have to do IS NULL, when you are performing a search for NULL values. I did a bunch of tests in my local database where I have NULL values. The only time that it worked is when I was using IS NULL or IS NOT NULL.
I am sorry I can't be more help (or if I'm just telling you what you already know), but it seems like you are going to have to write separate queries, or perhaps some simple logic to concatenate the appropriate WHERE logic, depending on whether a variable is null or not.

Categories