PHP Function Security Risks and Prevention Explained
PHP Cheat Sheet and Prevention Explained - Part 6
Foreword: In this part of the series, I explain PHP function security risks and how to prevent them.
By: Chrysanthus Date Published: 29 Jan 2019
Introduction
Risks are weaknesses from PHP or from you the programmer, that you may ignore; and attackers (hackers) would take advantage of.
Value Types
The different types of values (variables) mentioned in this volume are: Integer, Float, Boolean, string, null, resource, array, object and callable.
Remember that the string values '0', "0", '', "" are each equal to false but not identical to false. Null is equal to false, but not identical to false.
1, -1, '1', '-1', 'text' are each equal to true but not identical to true.
When coding functions in PHP, you have to note the type of the value you are sending as argument; you also have to note the type of the value the function is returning. This is to avoid misinterpretation within a function definition and misinterpretation outside the function definition (after returning a value): identity should not be confused for equality.
Solution: use the identity operators (=== and !==) for strict comparison in conditions and the equality operators (== and !=) for loose comparison, within a function definition or outside a function definition.
Checking Return Value
Assume that you have a function that will return any number from 0 to 9. Also assume that this function will return false, if an error occurs in its operation (for example if it has a bad input argument). Assume still that it would return true if it has an array full of the 10 numbers.
Now, with loose comparison, the return value 0 or false is the same as false. With strict comparison, 0 is different from false. With loose comparison any number above 0 or true is the same as true. With strict comparison true is different from these numbers. In order to make use of the return value, you have to test for all these 5 possibilities of numbers, with code similar to the following:
<?php
function fn($arg)
{
if ($arg === 0)
return 0;
if ($arg === 1)
return 1;
if ($arg === true)
return true;
if ($arg === false)
return false;
}
$ret = fn(0);
testFn($ret);
$ret = fn(1);
testFn($ret);
$ret = fn(true);
testFn($ret);
$ret = fn(false);
testFn($ret);
$ret = fn(5);
testFn($ret);
//testing return value
function testFn($argu)
{
if ($argu === 0)
echo "You can use 0 and not number > 0 or true or false<br>";
elseif ($argu === 1)
echo "You can use 1 and not number > 1 or 0 or true or false<br>";
elseif ($argu === true)
echo "You can use true and not number > 1 or 0 or false<br>";
elseif ($argu === false)
echo "You can use false and not number > 1 or 0 or true<br>";
else
echo "You can use the returned value as a number.<br>";
}
?>
The checking function uses strict comparison.
The output is:
You can use 0 and not number > 0 or true or false
You can use 1 and not number > 1 or 0 or true or false
You can use true and not number > 1 or 0 or false
You can use false and not number > 1 or 0 or true
You can use the returned value as a number.
Recursive Function
After at least 100 recalling in PHP, the script would stop running. Infinite recursion is considered a programming error in PHP. Comment at the bottom of this tutorial, and I will tell you how you can work-around (do not forget that you have to register, before you can comment).
Solution: do not code any recursive function that has more than 100 recursions. This is a limitation in PHP, which I think, can be worked-around still in PHP (comment below).
Argument Type Declaration
Problem of Converting One Argument Type to Another
Within a function definition, PHP can convert one type to another depending on the context. This can lead to wrong results. Try the following code:
<?php
function myFn($inte0, $inte1)
{
$sum = $inte0 + $inte1;
echo $sum;
}
myFn(2, 'text3');
?>
The output is:
Warning: A non-numeric value encountered in C:\Apache24\htdocs\temp.php on line 5
2
which is a warning message and wrong result - no fatal error.
The intention was to add 2 to 3 to have 5. Instead of passing, 3, 'text3' was passed. PHP converted it to 0. Attackers (hackers) like this. To prevent this, you have to do type declaration.
With type declaration, you specify the type that an argument must have in the parameter list. If the argument is not of the same type, you end up with a fatal error, and the program stops running; so that you do not have wrong results. Try the following code:
<?php
function myFn(float $flt, int $inte)
{
$sum = $flt + $inte;
echo $sum;
}
myFn(2.0, 'text3');
?>
The output is a fatal error (message).
Now try the following with the correct type of argument, sent, though as a string:
<?php
function myFn(float $flt, int $inte)
{
$sum = $flt + $inte;
echo $sum;
}
myFn(2.0, '3');
?>
The output is:
5
Types and Arguments
The parameter types with allowed arguments are as follows:
TYPE ARGUMENT
bool: argument must be a boolean value.
float: argument must be a floating point number.
int: argument must be an integer.
string: argument must be a string.
array: argument must be an array.
object: argument must be an object.
iterable: argument must be either an array or an instanceof Traversable.
callable: argument must be a valid callable.
Class name: argument must be an instanceof the given class.
Interface name: argument must be an instanceof the interface.
self : argument must be an instanceof the same class as the one the method is defined on.
Some scalar type have aliases. For example, the alias of float is double; the alias of bool is Boolean. Aliases are not supported in this scheme.
Strict Typing
Type declaration alone as solution still has its limits. For example, in the above code, instead of sending 3, '3' was sent and the function still worked, without even a warning message. In most such cases, you will have the correct result. However, there is a limit to every thing. To really enforce type declaration, you have to start the file with the statement:
declare(strict_types=1);
Try the following code where '3' is used in place of 3.
<?php
declare(strict_types=1);
function myFn(float $flt, int $inte)
{
$sum = $flt + $inte;
echo $sum;
}
myFn(2.0, '3');
?>
I tried the code and I had a fatal error; the script stopped working, as it should.
So, type declaration works at the limit, in strict mode. Try the following:
<?php
declare(strict_types=1);
function myFn(float $flt, int $inte)
{
$sum = $flt + $inte;
echo $sum;
}
myFn(2.0, 3);
?>
The output is:
5
best result.
Type Declaration and Included Files
Type the following code and save the file with the name, temp.php :
<?php
include ("temp2.php");
myt2(2.0, '3');
?>
Type the following code and save it with the name temp2.php in the same directory as the above file:
<?php
declare(strict_types=1);
function myt2(float $flt, int $inte)
{
$sum = $flt + $inte;
echo $sum;
}
?>
Run the temp.php file. The output is:
5
So, if an included file is in the strict mode, a call for a function in the included file from the main file, will not respect the strictness.
Solution: put both files in strict mode.
Problem of Converting One Return Type to Another
With return type declaration, you specify the return type that the function must return. If you return a type different from what you have specified, you end up with a fatal error, and the program stops running; so that you do not have wrong results. Try the following code:
<?php
function myFn($a, $b) : float
{
$joint = $a . $b;
return $joint;
}
$ret = myFn('some', ' text');
echo $ret;
?>
The output is a fatal error (message).
Now try the following where the return type is the same as the type (float) declared
<?php
function myFn($a, $b) : float
{
$sum = $a + $b;
return $sum;
}
$ret = myFn(2.5, 4);
echo $ret;
?>
The output is:
6.5
You declare the return type beginning with a colon, after the closing parenthesis for the parameters. Then you write the name for the type you want to return.
Types and Values
The return types with allowed values are as follows:
TYPE Values
bool: value must be a boolean value.
float: value must be a floating point number.
int: value must be an integer.
string: value must be a string.
array: value must be an array.
object: value must be an object.
iterable: value must be either an array or an instanceof Traversable.
callable: value must be a valid callable.
Class name: value must be an instanceof the given class.
Interface name: value must be an instanceof the interface.
self : value must be an instanceof the same class as the one the method is defined on.
Some scalar type have aliases. For example, the alias of float is double; the alias of bool is Boolean. Aliases are not supported in this scheme.
Strict Typing
Type declaration alone as solution still has its limits. Try the following code:
<?php
function myFn($a, $b) : int
{
$sum = $a + $b;
return $sum;
}
$ret = myFn(2.7, 3);
echo $ret;
?>
The output is:
5
instead of 5.7. The declared return type is, int. However PHP converts the float, 5.7 to integer, 5 by truncating the decimal part (which is not rounding - not correct).
In many such cases, you will have a satisfactory result. However, there is a limit to every thing. To really enforce type declaration, you have to start the file with the statement:
declare(strict_types=1);
Try the following code:
<?php
declare(strict_types=1);
function myFn($a, $b) : int
{
$sum = $a + $b;
return $sum;
}
$ret = myFn(2.7, 3);
echo $ret;
?>
I tried the code and I had a fatal error; the script stopped working, as it should.
So, type declaration works at the limit, in strict mode. Try the following, where the name (float) of the correct return type, has been used:
<?php
declare(strict_types=1);
function myFn($a, $b) : float
{
$sum = $a + $b;
return $sum;
}
$ret = myFn(2.7, 3);
echo $ret;
?>
The output is:
5.7
best result.
Type the following code and save the file with the name, temp.php :
<?php
include ("temp2.php");
$ret = myFn(2.7, 3);
echo $ret;
?>
Type the following code and save it with the name temp2.php in the same directory as the above file:
<?php
declare(strict_types=1);
function myFn($a, $b) : float
{
$sum = $a + $b;
return $sum;
}
?>
Run the temp.php file. The output is:
5.7
So, if an included file is in the strict mode for return type declaration, a call for a function in the included file from the main file, will respect the strictness. This is as opposed to argument type declaration, where the strictness is not respected.
Argument and Return Type Declaration Combined
With or without strict mode, you can combine argument and return types, as in the following code:
<?php
declare(strict_types=1);
function myFn(float $flt, int $inte) : float
{
$sum = $flt + $inte;
return $sum;
}
$ret = myFn(2.0, 3);
echo $ret;
?>
The output is:
5
For good results, use argument type declaration and return type declaration. For best results, use these in strict mode.
And if you have included files and you have control over the included files, then use strict mode for all files.
Callback Problem of Replacing One Argument Type with Another
Different types of values can be for the same argument. This can lead to wrong results. Try the following code, where the second argument to the main function was intended to be a callback, but was accidentally coded as a string with a variable that expands:
<?php
function mainfn ($variabl, $cb)
{
$str= 'We are learning.';
$colorArr = ['blue', 'green', 'red'];
echo $cb;
}
$var = 'John';
mainfn('dummy', "Mr. $var is here.");
?>
This kind of problem is rare, but it can still occur.
The output is:
Mr. John is here.
The callback variable type is now called, callable. You can prevent the above problem with argument type declaration as follows:
<?php
function mainfn ($variabl, callable $cb)
{
$str= 'We are learning.';
$colorArr = ['blue', 'green', 'red'];
echo $cb;
}
$var = 'John';
mainfn('dummy', "Mr. $var is here.");
?>
Note the use of the type, callable. I tried the code and I had a fatal error. The program stopped running and prevented wrong results.
Now try the following code, where $cb is actually a callback and the type callable has been used:
<?php
function mainfn ($variabl, callable $cb)
{
$str= 'We are learning.';
$colorArr = ['blue', 'green', 'red'];
$cb($str, $colorArr);
}
mainfn('dummy', function ($stri, $arr)
{
echo $stri, '<br>';
echo $arr[1], '<br>';
});
?>
The output is:
We are learning.
green
Everything worked fine with type declaration enforced.
The eval() Function
The eval() function takes statements in a string as argument and executes the statements.
Note: In case of a fatal error in the evaluated code, the whole script exits.
Warning: The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged. If you have carefully verified that there is no other option than to use this construct, pay special attention not to pass any user provided data into it without properly validating it beforehand.
The floor() Function
The floor() function returns a rounded-down number, or false in case of an error. So, do not consider false as 0 for the returned value. Test for the returned value with code similar to the following:
<?php
$variab = 0.6;
$ret = floor($variab);
if ($ret === false)
echo "Error occurred! Maybe argument is an array.<br>";
else
echo "Use $ret .<br>";
?>
The rand() Function
This function returns a pseudo (not genuine) random whole number between 0 and the value returned by getrandmax(). An attacker (hacker) can predict the returned value, though with some difficulties. If you want a returned value, where an attacker would predict with more difficulties, use the function:
int random_int ( int $min , int $max )
The function generates cryptographic random integers that are suitable for use where unbiased results are critical, such as when shuffling a deck of cards for a poker game.
The function returns a cryptographically secure random integer in the range min to max, inclusive.
That is it for this part of the series. We stop here and continue in the next part.
Chrys
Related Links
Basics of PHP with Security ConsiderationsWhite Space in PHP
PHP Data Types with Security Considerations
PHP Variables with Security Considerations
PHP Operators with Security Considerations
PHP Control Structures with Security Considerations
PHP String with Security Considerations
PHP Arrays with Security Considerations
PHP Functions with Security Considerations
PHP Return Statement
Exception Handling in PHP
Variable Scope in PHP
Constant in PHP
PHP Classes and Objects
Reference in PHP
PHP Regular Expressions with Security Considerations
Date and Time in PHP with Security Considerations
Files and Directories with Security Considerations in PHP
Writing a PHP Command Line Tool
PHP Core Number Basics and Testing
Validating Input in PHP
PHP Eval Function and Security Risks
PHP Multi-Dimensional Array with Security Consideration
Mathematics Functions for Everybody in PHP
PHP Cheat Sheet and Prevention Explained
More Related Links