Website Scripts And Cloud Tutorials

How to write secure PHP Scripts

Byadmin

May 20, 2011 ,

Last updated on April 8th, 2022 at 03:16 pm

Click to rate this tutorial!
[Total: 2 Average: 4.5]

In this tutorial I am going to walk you through some of the important things to remember while writing php code along with some security related filters and functions available in PHP. PHP has improved a lot especially with latest versions releasing built in features and functions like validating emails, ip address, checking html entities, filter_input options etc., Adding these filters take away lot of effort from the developers, instead of having our own logic for validating these variables spend valuable time developing the best user experience. All these details are available in PHP.net but I tried my best to summarize some of the important concepts for easier reference.

We will be talking more on these topics below with some best practices

Securing your variables

In most versions of PHP, you can access the value of a variable before it is initialized. Consider this simple example, assume that this is a login checker script which sets some secure variables / cookies or sessions before showing the original user profile page.

$logged_in=$_GET['logged_in'];
if ($password == $the_password) {
$logged_in = 1;
}
if ($logged_in == 1) {
// secure code to display user information stuff
echo "You are in secure page";
}

All an intruder has to do is just hit that login checker page directly and they will have access to those secure credentials. While this may seem obvious, it is an extremely common problem with PHP scripts.

If you check your logs you should see something like this (if error is enabled). Check how to Enable error reporting for more information.

[01-Apr-2022 17:45:50 UTC] PHP Notice:  Undefined variable: the_password in xxx.php on line 3
[01-Apr-2022 17:45:51 UTC] PHP Notice:  Undefined variable: password in xxx.php on line 3

The best way to prevent this is to always make sure variables are declared before they are used. For this example, you can just add the following line at the top of the file:

$logged_in = 0;

Also make sure that these variables ($password, $the_password)are defined before you do if condition. The issue in the above code is the condition is always true since both variables are undefined.

$password == $the_password

At least defining one variable will do it.

$the_password='adada'; //assuming it was taken from a MySQL or other database or any other source
$logged_in=$_GET['logged_in'];
if ($password == $the_password) {
$logged_in = 1;
}
if ($logged_in == 1) {
// secure code to display user information stuff
echo "You are in secure page";
}
else
{
echo "Not logged in";
}

Now the variable cannot be reset by a user since it is being declared before use.

Enable error reporting

Another recommendation is to enable error reporting. With the right setting, your scripts will generate an error if a variable is used before it is defined. While this might sound bothersome, it can be quite helpful for keeping things secure, since it will let you know of any variables you missed. If you are concerned about displaying errors / warning to end users at least try enabling errors in your test / development environment to make sure that everything looks good.

You can enable this for your entire server with a line in php.ini:

error_reporting = E_ALL

To enable this for a particular PHP script, just add this to the top of the file:

error_reporting(E_ALL);

If you do enable error reporting in your php.ini, but need it bypassed for a particular script, you can use this in your file:

error_reporting(0);

Once in production environment disable error display, but enable error logging to a file:

error_reporting(E_ALL);
ini_set('display_errors','Off');
ini_set('log_errors', 'On');
ini_set('error_log', '/path/to/error/log');

Form / Input handling

There are two superglobals which you will be using most of the time. $_GET and $_POST.

$_GET is used to retrieve variables passed in the URL.
$_POST is used to get values from html forms.

Other superglobal variables are

  • $GLOBALS
  • $_SERVER
  • $_FILES
  • $_COOKIE
  • $_SESSION
  • $_REQUEST
  • $_ENV

In the past, you also had $HTTP_POST and $GET_VARS but they are depreciated and should not be used.

Here is an example:

$the_name = $_GET['name'];

Keep in mind however that you need to make sure the value exists in the superglobal array before you use it, or you may receive an error. Try this:

if (isset($_GET['name'])) {  $the_name = $_GET['name']; } else { $the_name = ""; }

Now that you know how to get input from the user properly, there is still the matter of someone being able to pass random variables to your script. Use filter_input , instead of using $_POST, $_GET etc., directly

This is easy to fix with the register_globals option of PHP. With register_globals disabled, the only way you will be able to accept user input is with the superglobals. PHP has depreciated register_globals.

$get=filter_input(INPUT_GET, 'search',FILTER_SANITIZE_SPECIAL_CHARS);
echo $get."<br>";
echo $_GET['search'];

Save the file as get.php and run the above code by passing get.php?search=check %26 this . View the source of the page in a browser to see the difference in results. More details on filter_sanitize

Use FILTER_CALLBACK to call user defined function

If you want your own function to be called from filter_input you can make use of this option.

Click here to know more about this option

Use htmlspeacialchars function

This function in PHP converts special characters to HTML entities.

$convert = htmlspecialchars("<img src='https://demo.mistonline.in/check_img'>", ENT_QUOTES);
echo $convert;
//OutPut
&lt;img src=&#039;https://demo.mistonline.in/check_img&#039;&gt;

ENT_QUOTES Will convert both double and single quotes. More details can be found on PHP documentation

User Filter_validate to validate URL, Domain, Email , IP etc.,

Its pretty easy to validate domain / email / ip address. No need of adding any complex checks of your own now. Check this example

If I add $email = “[email protected]”; notice the .in missing in the email. The output of the below script will be bool(false)

if(filter_var($email, FILTER_VALIDATE_EMAIL)){
    echo $email.'<br>';
    var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
}else{
    var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
}

If I pass a valid email address ([email protected]) output will be

More details about other valid filters can be found in this document.

Exposing Sensitive Information

When you store sensitive information such as database passwords and other credentials in files and imagine If these files are not properly secured, an attacker could see the contents of them, therefore hacking the applications database, etc.

Make sure to close down all the ports(especially MySQL / any database ports) from external access except HTTP / HTTPS. SSH needs to be confined only to your IP / organization CIDR.

The most common file extension for php include files is .inc. By using this extension and not properly creating parsing rules in Apache, a developer could create a major security hole in the web application.

In Apache configuration the default file type for unknown file extensions is text/plain. If the .inc file is not set to be parsed as a PHP file and it is in the document root then we can access this file and see the contents of it by visiting the corresponding URL.

The best solution to this problem is to store these files outside of your document root (e.g. /www, /public_html, etc.). A best practice is to place the most essential files in your document root.

If you don’t have access outside your document root then at least use the following 2 methods:

1. Use an extra .php extension on the end of your file. E.g. sensitive.inc.php
2. Secure the .inc file in a .htaccess file if using Apache

<Files ~ "\.inc$">
Order allow,deny
Deny from all
</Files>

3. If using nginx use something like this

location ~* \.(inc|txt)$|/\. {
    deny all;
}

Suggestion: Move all your sensitive information outside of your document root, if that is not possible add an extra .php extension to your .inc files and/or secure them in a .htaccess file, if using Apache or if using nginx use deny statement as show above.

Note: This is not an exhaustive list related to security but something to start with

Click to rate this tutorial!
[Total: 2 Average: 4.5]

Leave a Reply

Your email address will not be published.