PHP Security 101 + 1

php security

PHP is by far the most widely used server-side programming language. Security threats surrounding PHP applications have been in the news because PHP captures over 80% of the internet with over 10 million websites built. It’s no wonder that with such a massive usage, PHP is also one of the most targeted and exploited languages, as shown in the below statistics:

top reported vulnerabilities per language

source: https://insights.dice.com/2019/11/27/programming-language-vulnerabilities/

One of the main reasons PHP applications are often vulnerable is that secure coding practices are not followed in PHP development across the entire application. Common vulnerabilities like SQL injection, Cross-site scripting, data tampering etc., can be avoided just by adding a few lines of code to protect the application and server data.

In this article, we will cover the areas a web developer needs to focus on to better secure their PHP applications.

Keep PHP up-to-date

First and foremost, developers should use the latest stable release of PHP in their applications. This is because new releases often contain security patches and bug fixes for known security vulnerabilities that can be exploited by an adversary if not updated on time.

Restrict sensitive files

When deploying a PHP application on a live server, make sure to move all the application, configuration files and other needed sensitive files to a folder that is not publicly accessible (review the folder/file permissions). For example, the folder structure could follow like this example below:

app/
  config/
    parameters.yml
  src/
public/
  index.php
  style.css

Doing so makes it difficult for attackers to access sensitive information outside the root. 

Enable SSL certificates

For all web applications, the traffic being sent to and from the web servers should be encrypted using TLS/SSL certificates. This means that the entire application should use the HTTPS protocol so that sensitive information can not be leaked or sniffed by an attacker.

Configure document root properly

The document root folder for PHP applications should be set to /var/www/html for Linux systems and C:\inetpub\wwwroot for Windows systems. Doing so will ensure that all sensitive PHP files and other files containing environment variables and database credentials such as .htaccess and .env are not accessible.

Turn off verbose errors and enable logging.

Verbose error messages are one of the reasons that attackers gain valuable information about the website, which help the attacker is conducting further attacks. Whenever an application is deployed on a live server, make sure to disable all verbose errors and display custom errors instead. 

display_errors=Off

Additionally, configure the application to log all error messages so that the relevant teams can go through the logs to detect any anomalies or unsuccessful as well as successful attacks.

log_errors=On
error_log=/var/log/httpd/php_application_errors.log

Implement URL encoding

Always use PHP built-in functions like urlencode() to safely encode all strings that are used in forming URLs. This can benefit in evading attacks that use symbols and other characters.

vulnerability scanning

Do not trust the user input

The majority of PHP attacks happen because developers trust user input and process or parse it directly into the PHP file. In doing so the malicious code also gets processed resulting in nasty surprises, what we call as exploitation.

PHP web developers should work under the assumption that all inputs coming from the client end is malicious and therefore should be sanitised as soon as they arrive. Input sanitisation should be the immediate next process after an input is received.  

Similarly, user input is also displayed in the application as output. This output data should also be sanitised before displaying and rendering on the application. Unlike input sanitisation, output sanitisation should be done as late as possible to ensure that the output data is not modified at any point after sanitisation.

The rule of thumb should be to sanitise input as early as possible and encode the output.

Sanitise, validation and escaping

The three terms sanitise, validate and escape are seen very commonly when talking about secure coding, but what does each of these terms mean.

Validation

Validation means checking whether the application is receiving the right kind of input data. For example, if the application is asking for a person’s age, only integer values should be received and there should be no alphabets or symbols in the input. PHP functions like is_numeric() can be used here.

Sanitisation

Sanitisation means to strip or remove potentially dangerous characters from the input. Such as removing apostrophes (‘) to stop SQL injections or angle brackets ( < , >) to stop XSS attacks.

Escaping

Escaping means converting harmful data into harmless data. For example, if any attackers add 
$search = $_GET[‘search’] ?? null;
echo ‘Search results for ‘.$search;

// This can be mitigated using the htmlspecialchars function
$search = htmlspecialchars($search, ENT_QUOTES, ‘UTF-8’);
echo ‘Search results for ‘.$search;

In the above code functions such as ENT_QUOTES can be used to escape single and double quotes so the attacker can not break the syntax and execute scripts.

SQL injection

As discussed above SQL injection attack is very common in PHP scripting and can be avoided by using parameterised queries or prepared SQL statements.

$sql = "SELECT username, password FROM users WHERE id = :id";
$sth = $dbh->prepare($sql, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]);
$sth->execute([':id' => $id]);
$users = $sth->fetchAll();

Directory traversal

Directory traversal or dot, dot, slash (../) attacks can reveal sensitive information and files within the webserver directory.  These attacks can be mitigated using the following:

// Check if the string contains parent directory
if (strstr($_GET['page'], '../') !== false) {
    throw new \Exception("Directory traversal attempt!");
}

// Check for remote file inclusions
if (strstr($_GET['page'], 'file://') !== false) {
    throw new \Exception("Remote file inclusion attempt!");
}

// Use whitelists of web pages that are only allowed to be included
$allowed = ['home', 'blog', 'contact', 'services'];
$page = (in_array($page, $allowed)) ? $page : 'home';
echo file_get_contents('../pages/'.$page.'.php');

Use a combination of basename() and realpath() functions, but this doesn’t help you from LFI (Local File Inclusion) issues. 

Command injection

Executing untrusted commands can directly lead to compromising the webserver. Be sure not to trust any command that is unfamiliar.

exec('rm -rf '.$GET['path']);

Code injection

Functions such as eval() should be avoided at all costs, as they can be used to execute arbitrary code. If this is unavoidable then make sure to sanitise the input properly before using PHP eval() function.

eval('include '.$_GET['path']);

Further, for avoiding code injection attacks, you need to disable functions in the PHP configuration that allow direct processing of input or interaction with the backend. These dangerous functions examples are exec(), shell_exec(), passthru(), and system(). 

Storing passwords

All passwords stored or used in the application should be encrypted or hashed. The password_hash() function can be used for this. Password hashing ensures that even if the password is leaked, an adversary can not reveal the actual value of the password.

Errors and logging

As mentioned above, detailed errors give attackers insight into the application and can be used to conduct further attacks. Always turn off the error display and make sure to log all errors in a separate file.

; Disable displaying errors to screen
display_errors = off
; Enable writing errors to server logs
log_errors = on

Disclosed PHP version

PHP versions are visible by default in the PHP configuration in the HTML headers, however, it is a best practice to hide these. If an attacker knows the technology version they can find publicly disclosed vulnerabilities associated with that version and use the exploit on the application.

expose_php = off

Remote File Inclusion (RFI)

Access to remote files should be disabled so that an attacker can not load their own hosted files into the web page. In an RFI (Remote File Inclusion) attack, an attacker can include their own files and render those in the context of the application.

; disabled opening remote files for fopen, fsockopen, file_get_contents and similar functions
allow_url_fopen =  0

; disabled including remote files for require, include and similar functions
allow_url_include = 0

User sessions configuration

There are a few areas in sessions management that should be considered when writing secure code:

  • Enable using sessions cookies with a unique session ID
session.use_cookies = 1
session.use_only_cookies = 1
session.use_trans_sid = 0
session.use_strict_mode = 1
  • Enable cookies that are httponly, so that malicious JS code can not steal the cookies.
session.cookie_httponly = 1
  • Set the cookies domain value, instead of using a wildcard.
session.cookie_domain = thecyphere.com
  • Enable secure cookies so that the cookies are only transmitted via HTTPS
session.cookie_secure = 1

Secure file upload

Uploading user-supplied files is a feature that almost all applications have. These files should be uploaded in a secure manner so that no malicious executable or PHP script is uploaded to the webserver.

Files should be validated using mime types:

$finfo = new finfo(FILEINFO_MIME_TYPE);
$fileContents = file_get_contents($_FILES['some_name']['tmp_name']);
$mimeType = $finfo->buffer($fileContents);

And the upload.php should be:

foreach ($_FILES['pictures']['error'] as $key => $error) {
  if ($error == UPLOAD_ERR_OK) {
      $tmpName = $_FILES['pictures']['tmp_name'][$key];
      $name = basename($_FILES['pictures']['name'][$key]);
      move_uploaded_file($tmpName, "/var/www/project/uploads/$name");
  }
}

In short, here is the advice for secure file uploads:

  • Restrict file types accepted for upload, use an allowlist instead of a blocklist – check the file extension and only allow certain file formats to be uploaded.
  • Ensure a malware scanner is configured to scan contents before saving and sharing the uploaded content. This will verify contents are not malicious or uploaded under the wrong file format to evade file upload types.
  • Check for double extensions, files without a filename such as .htaccess, web.config, etc.
  • Consider renaming the uploaded files and do not allow execution/change permissions.

Conclusion

PHP security practices is a vast topic, and we have only scratched the surface. In the end, it is the responsibility of the PHP developers to create code that not only fulfils the business requirements but is also secure, and resilient against cyber attacks. Your developers should never trust the user’s input and keep security issues such as remote file inclusion, XSS, CSRF, SQLi, URL encoding etc. in your mind.

Remediating such vulnerabilities for smaller businesses is sometimes a challenging task, specifically WordPress sites or similar stacks with PHP aren’t easily doable. Rather than loading another plugin, it would make sense to perform changes at code level by hiring a good developer.  

Also, consider getting a secure code review along with penetration testing activities conducted on your PHP applications. Get in touch for independent validation of your application security controls before it’s too late.

Web Application Penetration Testing

Article Contents

Sharing is caring! Use these widgets to share this post
Twitter
LinkedIn
WhatsApp
Email
Scroll to Top