PHP basic security

Let's review common mistakes and their prevention methods.

Echo user input

Below code is enough to create a XSS vulnerability in your website.

echo '<p>' . $_GET['name'] . '</p>'; // vulnerable to XSS

Never directly use user input for generating response contents. You have to use proper encoding on user input to escape any dangerous code. PHP has the build-in htmlentities() function to encode html special characters which you can use.

echo '<p>' . htmlentities($_GET['name']) . '</p>'; // safe code

Sometimes user input is used indirectly for generating a page. For example, user input might be saved in a database before being used in the response.

Using user input in file paths

Generating and using file paths that contain user inputs is one of the most dangerous mistakes that can cause critical vulnerabilities like:

Below code is an example of a file inclusion vulnerability.

include $_GET['file']; // vulnerable to remote/local file inclusion

To prevent file inclusion vulnerabilities:

$f = $_GET['file'];
if(1 == $f) {
    include __DIR__.'/filename1.php'; // safe
} else {} //...

Concatenating SQL queries with user input

Creating SQL queries using user input allows users to manipulate the original SQL command and inject their arbitrary command. This is called SQL Injection. Hackers can exploit SQL Injection vulnerability to execute commands like drop on the database or execute system commands.

Example code:

$db = new SQLite3('mydb.db');
$id = $_GET['id'];
// below line is vulnerable to SQLI
$name = $db->querySingle("SELECT name from products where productId=$id");
 
// below line is not vulnerable
$name = $db->querySingle("SELECT name from products where productId='" .
SQLite3::escapeString($id) . "'");
 
echo $name ;

A method to avoid SQLI is to escape special characters in the user input (like the above example).

The accepted best practice is to use prepared statements for creating SQL queries to prevent SQL Injection.

$smt = $db->prepare("SELECT name from products where productId=:id");
$smt->bindValue(':id', $id, SQLITE3_TEXT);
$smt->execute();

Executing user input

Below commands in PHP allow execution of either PHP code or OS commands.

eval()
preg_replace()
system()
exec()
passthru()
shell_exec()
popen() // also be used for command execution indirectly.

Passing user input as arguments to the above functions can cause command execution vulnerabilities where hackers can execute arbitrary commands on the server. Consider below code as an example:

$ip = $_GET['ip'];
echo exec("ping $ip"); // vulnerable code

The above code is a ping service where users can ping any IP. If a user enters 1 & echo 123 as an IP address then the command echo gets executed on the server.

How to prevent command execution vulnerabilities:

$ip = $_GET['ip'];
if (filter_var($ip, FILTER_VALIDATE_IP)) {
    echo("$ip is a valid IP address \n");
    echo exec("ping $ip");
} else {
    echo("$ip is not a valid IP address");
}

Redirecting to user input

Redirecting a user in PHP is common and easy. example:

header('Location: ' . $_GET['url']); // vulnerable code

It can be exploited to redirect the user to any other website. To prevent open redirections, make sure the URL is not an off-site url before redirecting the user.

Turning on error reporting

PHP errors disclose information like path/files, database errors, OS type, and some other information. Displaying any kind of PHP errors on production helps hackers to break into your website easier.

error_reporting(E_ALL); // ok for DEV, bad code for production
error_reporting(); // safe for production usage

phpInfo()

The phpInfo() function displays a huge amount of information. PHP version number, active extensions, configurations, and system paths are a few examples of such information. This information can be used by hackers to learn about the server and craft their attacks to be more successful. To avoid any information disclosure it's better to never use the phpInfo() function on production servers.

Conclusion:

Dealing with user inputs in PHP applications can be tricky and prone to different vulnerabilities. XSS, SQL Injection, and local file inclusion are a few of explained vulnerabilities related to user inputs. Make sure you have strict checking for user inputs and avoid using risky functions like eval() and shell_exec() to have a more secure code.


Tags:
PHP

Blog Disclaimer:

Le opinioni espresse nel mio blog sono solo questo: mie opinioni.

In nessun modo rappresento le opinioni dei miei clienti in questa sede.


Notice: Undefined variable: browserName in /var/www/taziomirandola.it/lib/Visitors.php on line 86

Notice: Undefined variable: browserName in /var/www/taziomirandola.it/lib/Visitors.php on line 96

Deprecated: strripos(): Non-string needles will be interpreted as strings in the future. Use an explicit chr() call to preserve the current behavior in /var/www/taziomirandola.it/lib/Visitors.php on line 96

Notice: Undefined index: HTTP_ACCEPT_LANGUAGE in /var/www/taziomirandola.it/lib/Visitors.php on line 39

Fatal error: Uncaught TypeError: Argument 1 passed to safe_text() must be of the type string, null given, called in /var/www/taziomirandola.it/lib/Visitors.php on line 39 and defined in /var/www/taziomirandola.it/lib/Visitors.php:162 Stack trace: #0 /var/www/taziomirandola.it/lib/Visitors.php(39): safe_text() #1 /var/www/taziomirandola.it/lib/Visitors.php(124): Visitors::getData() #2 [internal function]: Visitors::log() #3 {main} thrown in /var/www/taziomirandola.it/lib/Visitors.php on line 162