• April 14, 2023
  • For programmers

20 most important security mistakes in PHP

20 most important security mistakes in PHP

Welcome to the article in which we discuss the top 20 risks in PHP applications that every developer or startup owner should be aware of. No matter what stage of development your application is at, or how large and how experienced the team is working on it, it is worth checking that we have thought of and taken proper care of everything. Application development is a continuous process, and it is important to be constantly aware of possible problems and potential holes in the system so that no one uses them against us.

Even if you are not a developer but the owner of a startup or technology company, the responsibility for ensuring that your application is secure remains on you. Of course, without technical knowledge, you won't be able to do it yourself, but with the content in this article, you'll know what to keep in mind and what you need to make sure you do.

Please remember! At the bottom of the article, there is a checklist available that you can download to help you secure your application before launch.

Welcome!

Cross-site scripting (XSS)

Cross-site scripting is a method of a hacking attack that, using carefully prepared code makes it possible to inject code into a page through, for example, a form or a variable in the URL that performs an undesirable action for the application.

We can categorise this risk into the:

SQL Injection

The most common type of XSS attack involves injecting a command into a database query that will retrieve or, for example, delete data from the database. This type of attack happens most frequently, so we will cover this problem in a separate section of this article.

Code Injection

Injecting code into an application that has not been properly secured. We can inject code through places in the application that accept some sort of user data. This could be a variable in a URL or it could be through a form.

Command Injection

This is the injection of code into a command that will be called on the server. It allows, among other things, viewing the content of files, server settings or deleting server content. This type of XSS attack works on the same basis as the previous two. In the place where some data is accepted from the user, the attacker inserts specially prepared code, which will be called directly on the server, thus enabling access to server resources (e.g. a list of files).

How to protect yourself: Take care of all user-derived variables, that is:

  • variables from the URLs,
  • content stored in biscuits,
  • data sent via a form,

All of these should be properly secured. Special characters, HTML code and anything not needed for the application to function properly should be removed.

In addition, the format of the transmitted data must be taken care of. For example, if the user ID is expected in the URL, the system should not transmit anything that is not a number.

You can read more about this method in the article PHP injection, what is this.


SQL Injection

SQL Injection is the most popular XSS attack method, which, by using specially prepared code in place that accepts some sort of input from the user, enables the attacker to perform certain actions in the database. This can include, for example, the viewing or exporting of the database, but also the clearing of the entire contents of the database.

For instance, if we have an online shop in which the address to the category looks like this:

https://our-show.com/products/Categoryname

the Categoryname variable is the name of the category from which the products are to be displayed.

Unless we properly protect the production category name variable and forward the name directly to the SQL query, e.g. like this:

$pdo->query(‘SELECT * FROM products WHERE categoryName = “‘ . $categoryName . ‘”’);

by adding a specific code to the category name, we can influence which query will be executed.

By modifying slightly our URL, we can download all records from the products table:

https://our-show.com/products/bikes” or 1=1

By entering such a product name, our query will eventually look like this:

SELECT * FROM products WHERE categoryName = “bikes” or 1=1

As condition 1=1 will always be met, we will get all the products that are in the products table.

By changing a little our address to:

https://our-show.com/products/bikes”; truncate users;

To the query that retrieves products, we will add another one that cleans the contents of the table 'users'.


Cross site request forgery XSRF/CSRF

A CSRF attack is an operation where a request is sent to an application with specially modified data.

Let's say we have an app where users can send money to each other. The application is well secured, all data is filtered, users are verified and it may seem that the application is safe. When we are logged in to this application and we access a page that tries to perform an attack, e.g. using a hidden form that will be sent to our application. This form will send a request to transfer money to another user. The data will be properly crafted, though it will be sent in the format the application expects so the security in the code will think that everything is ok. Since you were logged in and the request was sent correctly, it was your money that went to another user.

How to protect yourself: To protect against this issue, a token generated and stored in the session should be added to every request sent (e.g. via a form or AJAX). Upon receiving a request, the application should check that it has received the token and that the token is of the correct value. If not, the request has been sent incorrectly and should not be processed.


Hide Files and folders from the Browser

The servers on which PHP applications run display the contents of the folders and files that are available within them. This way, users can access the contents of our application or our server. Everything is fine if users can only access what we want them to. When running an application in production, you need to block all folders that users cannot access.

As an example, our application stores user sessions in files in the storage/sessions folder. If this folder is not secured, the user will see a list of all active user session files after changing the URL to domain.com/storage/sessions. He will be able to view their contents and log in to the selected user's account without any problems.

How to protect yourself: Blocking access to a folder is very simple and even a non-technical person can do it. All you need to do is create a .htaccess file in the folder you want to lock access to and paste in it:

Deny from all

Securely Upload Files

If we have a form in our application that allows the respondent to upload a file, we need to do everything we can to ensure that only the files we expect are uploaded to the server.

Let's assume that a user can set up their profile picture in our application. The photo must be publicly available, so the system uploads it to the /public/avatars folder. The address to such a file will be available to everyone. If the system does not filter the uploaded files, the user will be able to upload a suitably prepared php file, e.g. evil.php and then enter the address https://domain.pl/avatars/evil.php. If the file is simply uploaded, an attacker can put anything in it. List all the files and their contents, view configuration data, make a copy of the database, etc.

How to protect yourself: In this case, several elements need to be taken care of:

  1. Verify file type and extension, if you want to upload images then only accept images.
  2. Rename files so that the file name on the server differs from the name the user has entered.
  3. For images, use the getimagesize function, for example, to check that the image is correct.
  4. Set a limit on the size of files, especially for files that should not be large.
  5. Store files on the server in a location that is not publicly accessible.
  6. Use the ForceType command in Apache and mime.types in Nginx to protect access to specific file types.

Document Root Setup

Every application has a specific file and folder structure. We have a folder with code, a folder with configuration, a folder with cache files, etc. All of these files should be a level below the access to which we have set our VHOST.

For instance, if our application is structured like this:

~/app
     ~/controllers
     ~/config
     ~/storage
     ~/index.php

Placing the index.php file in the root folder is a mistake.

How to protect yourself: The main file (e.g. index.php) and host address should be set to the public folder (e.g. /app/public) and not to the folder with the application (/app). This way, the user cannot access the application but only the public content (i.e. CSS or JS files).


Displaying logs in production

Any version of an application that is available to the public must have error handling disabled. Even if we think our application is well protected, an experienced hacker will certainly find a way to break through our security. And with the error display turned on, we will make things easier for him because he will know the structure of the application, have a look at the code of some files or even, in extreme cases, the configuration file.

How to protect yourself: In public versions of the app, disable the showing of errors, this can be done in two ways:

  1. In the php.ini file, set the value of the display_errors and display_startup_errors variables to false.
  2. In the PHP code in the main file, the error_reporting function can be called with the value 0 passed in.

Public staging/dev version

When developing an application, we usually have a DEV/STAGING version in addition to the production version, on which the developers test the changes before uploading them to production. If we do not secure the test versions, an unauthorised user may gain access to it.

How to protect yourself: The best option is to set up access to the trial versions only for specific IP numbers or for a single IP and connect to the trial versions via VPN.


Public MySQL/PMA

Likewise, just as test versions of the application should not be public, access to the database should only be granted from the server IP and over a VPN. Furthermore, any PHPMyAdmin-type control panel should be avoided and the database should be connected via external programmes.

How to protect yourself: Provide access to the database by IP only, not publicly.


Password hash

Although everyone knows this in theory, there are still a lot of applications that store passwords as MD5 hash. This is certainly better than keeping passwords in plain text, especially if passwords are additionally protected with salt, but it is not a good solution.

How to protect yourself: Keep passwords in the database properly encoded, and secured with salt.


Session hijacking

Session hacking or session hijacking is a method that involves illegally gaining access to another user's session. This occurs when a hacker gains access to another person's session ID and then uses that ID to present himself to the server as another person.

There are several ways to access another person's ID:

  • XSS or Cross-Site Scripting - This type of attack involves injecting malicious code, usually JavaScript, into the attacked page. Thanks to specially prepared code, the attacker can see the cookie of the PHPSESSID variable and then send it to himself.
  • Session Sidejacking - An attack involving listening to requests sent to the server, which, if the traffic is not encrypted, allows the session ID to be intercepted.
  • Session Fixation - This situation can occur when an application, instead of requiring a new session ID during user authentication, accepts an existing ID. An attacker will use the existing ID and modify it in a way to gain access to another user's session.
  • Session Prediction - This method is more difficult because it requires the attacker to know how the application generates the session ID. Having such knowledge, this person may try to guess the ID of other users and impersonate them.

How to protect yourself:

  • XSS or Cross-Site Scripting - Make sure that validation is correct where users can insert their data and inject some code. This way, the system will not let anything through that could be a security risk.
  • Session Sidejacking - You can protect your application against this type of attack by installing a verified and validated SSL certificate.
  • Session Fixation - Remove any identifiers that may be publicly available, such as in a GET address or form. Additionally, it is a good idea to set the variable session.use_trans_sid to 1 and session.use_only_cookies to 0 in the server configuration.
  • Session Prediction - To avoid this type of attack, long session IDs with a high level of randomness should be used. A version of PHP 7.1 onwards ensures that session IDs are reasonably secure.

Exposing PHP version

The more information a person trying to carry out a hacking attack on our application or server has, the easier his job is. There is an expose_php option in the PHP configuration, which, if disabled, will prevent our server from sending information about itself.

When sending a request to the server, if the expose_php option is enabled, we get some information about the type and version, both of the server and PHP:

Apache/2.2.0 (Unix) PHP/6.0.0 PHP/6.0.0-dev Server at www.example.com Port 80

With the expose_php setting, this information will not be passed on.


Remote files

The allow_url_fopen and allow_url_include functions are enabled to allow external files to be loaded using the FTP and HTTP protocols. By disabling these functions, even if there is a security breach of our application, the server will not allow external files to be loaded.


Open base dir

The open_basedir command allows us to define in the PHP configuration which folders our code will not access, such as in the open function. This is a very good security feature that imposes an extra level of protection, beyond the application code, which will work if the system has security vulnerabilities.


SSL certificate

An SSL certificate is a so-called bleeding obvious thing but it is worth mentioning. Every application or even website must have an SSL certificate. It is not a big cost, the installation of the certificate on the server is very simple and the benefits in terms of security are great.


Old PHP version

Every new version of PHP not only introduces new features or brings optimisations, but also fixes bugs or vulnerabilities that have been discovered. Keeping libraries and programmes on the server up to date, and updating or uploading fixes as soon as they are released, reduces the risk of exploiting security holes in any software.


PHP Object Injection

PHP Object Injection is a method that uses serialisation. If we save some data in a serialised form in our application, we need to secure what users provide in that data. If the serialisation is not protected, the user may inject an object that is stored in the serialisation and then deserialised elsewhere in the application, causing unwanted events.

How to protect yourself: As with XSS attacks, the key to avoiding such data is to clean and validate all data that the user may transmit to our application.


Don’t rely on cookies for security

The contents of cookies can be very easily viewed and, even worse, these contents can be very easily modified. Therefore, storing important data in cookies is not a good idea. And if you must store something, you should not add anything in the public form to the cookies.

For example, we need to store information about an actively logged-in user in a cookie. If we simply put the user ID in the cookie, there is a risk that someone will swap the cookie content with a different ID and gain access to the content they should not have (e.g. admin panel). If, instead of a user ID, we insert a token, which is unique for each user, we make it difficult for the intruder to swap the value. A numeric ID can be easily changed to another, but a 32-character token will no longer be possible to guess.

How to protect yourself: Certainly, do not store anything important in cookies. But if we must, not in a public format. When creating a cookie, it is also worth bearing in mind to:

  1. Set the domain and path so that the cookie cannot be overwritten from the outside.
  2. Set the cookie to expire as soon as it is no longer needed.

Use allowlists instead of blacklists

Both on the server and in the application, it is common to block access to certain resources, e.g. the IP address from which the server receives a lot of suspicious requests. In order to increase security, we can reverse this situation, i.e. in resources that allow this (e.g. database access), we do not create a list of blocked addresses, but create a list of addresses that can access the given resource.

Additionally, it is a good idea to ensure that anyone accessing a resource (e.g. a server or database) connects to it via a VPN instead of directly.


No error control

Last, but not least. No matter what safeguards are in place and no matter what steps are taken, there is always the risk of errors occurring which can be taken advantage of by the wrong people. This is why it is important to use good bug detection software. This allows you to know in advance if an undesirable situation arises, and with information about the error, the developers will be able to react to it faster.

The ideal solution for this is Streply, which will not only catch any errors that occur in your application but also, thanks to notifications, you will know about it immediately. This will allow you to react and correct the bug immediately.

I hope this article will help you avoid the most common mistakes that affect the security of PHP applications. The information contained in this article is worth implementing not only at the beginning when the application is created, but these points should also be checked in an already running application. The more risks we reduce, the more secure our application will be.

Download our checklist

Are you ready to level up your PHP software?

Create an account, test Streply entirely free for 30 days, and then pay only 9$ per month.

Bugs
Logs
Performance
Try for free in 30 seconds

Pricing

You only pay for what you use

Monthly usage

100k requests

Logs retention

14 days

Price

9$ per month

Monthly usage

500k requests

Logs retention

30 days

Price

24$ per month

Monthly usage

1M requests

Logs retention

60 days

Price

49$ per month

Monthly usage

5M requests

Logs retention

90 days

Price

99$ per month

Monthly usage

10M requests

Logs retention

180 days

Price

149$ per month

Monthly usage

+10M requests

Logs retention

360 days

Price

negotiable

Monthly usage

100k requests

Logs retention

14 days

Price

90$ per year

Monthly usage

500k requests

Logs retention

30 days

Price

240$ per year

Monthly usage

1M requests

Logs retention

60 days

Price

490$ per year

Monthly usage

5M requests

Logs retention

90 days

Price

990$ per year

Monthly usage

10M requests

Logs retention

180 days

Price

1490$ per year

Monthly usage

+10M requests

Logs retention

360 days

Price

negotiable

  • Free 30-days trial
  • No credit card required
  • Cancel anytime
Blur