Vulnerabilities in PHP code are usually caused by a mistake that a developer made when writing the original code. It is quite common for a developer to launch a perfectly working PHP application like WordPress. But do not anticipate all the ways that hackers on the internet will try to gain access.
As the application uses more and more, the developer will learn from users and their experiences with attacks how the application can be made more secure. Developing a PHP application is an evolutionary process, which is why offensive360 is important to keep abreast of security alerts.
Vulnerabilities in PHP will generally group into categories based on their type. Below is a list of the most common kinds of vulnerabilities in PHP code and a basic explanation of each.
When writing PHP code it is very important to keep the following security vulnerabilities in mind to avoid writing insecure code. Now let’s look at some common vulnerabilities in more detail. These are the most common vulnerabilities in PHP you’ll encounter when writing PHP code. We’ll discuss a few in further depth below.
Cross-Site Scripting is a type of vulnerability in a web application that will cause by the programmer not sanitizing input before outputting the input to the web browser (for example a comment on a blog). It will commonly be used to run malicious javascript in the web browser to do attacks. Such as stealing session cookies among other malicious actions to gain higher-level privileges in the web application.
A blog allows users to style their comments with HTML tags, however, the script powering the blog does not strip out <script> tags allowing any user to run javascript on the page. An attacker can use this to their advantage to run malicious javascript in the browser. They could infect users with malware, steal session cookies, and more.
<script>
alert('Cross Site Scripting!');
</script>
In PHP there are two primary functions, htmlspecialchars() and strip_tags(), built in to protect yourself from cross-site scripting attacks. The htmlspecialchars($string) function will prevent an HTML string from rendering as HTML and display it as plain text to the web browser.
htmlspecialchars() Code Example:
<?php
$usercomment = "<string>alert('Cross Site Scripting!');</script>";
echo htmlspecialchars($usercomment);
?>
The other approach is the strip_tags($string, $allowedtags) function which removes all HTML tags except for the HTML tags that you will whitelist. It’s important to note that with the strip_tags() function you will be more careful, this function does not prevent the user from including javascript as a link, you’ll have to sanitize that on your own.
strip_tags() Code Example:
<?php
$usercomment = "<string>alert('Cross Site Scripting!');</scrit>";
$allowedtags = "<p><a><h1><h2><h3>";
echo strip_tags($usercomment, $allowedtags);
?>
Setting the X-XSS-Protection Header:
In PHP you can send the X-XSS-Protection Header which will tell browsers to check for a reflected Cross-Site Scripting attack and block the page from loading. This does not prevent all cross-site scripting attacks will only reflecting ones and will use in combination with other methods.
<?php
header("X-XSS-Protection: 1; mode=block");
?>
Another option, if you like more control over how the sanitization works, is to write your own HTML Sanitization function, this is not recommended for PHP Beginners as a mistake will make your website vulnerable.
An effective approach to preventing cross-site scripting attacks, which may require a lot of adjustments to your web application’s design and codebase, is to use a content security policy.
The most common way of setting a Content Security Policy is by setting it directly in the HTTP Header. This can be done by the webserver by editing its configuration or by sending it through PHP.
Example of a Content Security Policy set in an HTTP Header
<?php
header("content-security-policy: default-src 'self'; img-src https://*; child-src 'none';");
?>
You can include your Content Security Policy in the page’s HTML and set it on a page-by-page basis. This method requires you to set it on every page or you lose the benefit of the policy.
Example of a Content Security Policy set in an HTML Meta Tag
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src">
Session Hijacking is one of the most common Vulnerabilities in PHP will cause by an attacker gaining access to a user’s session identifier and being able to use another user’s account impersonating them. This will often use to gain access to an administrative user’s account.
To defend against Session Hijacking attacks you need to check the current user’s browser and location information against information that will store about the session. Below is an example implementation that can help mitigate the effects of a session hijacking attack. It checks the IP Address, User-Agent, and if the Session Expired removing a session before it’s resumed.
Php code:
<?php
session_start();
if ($_SERVER['REMOTE_ADDR'] != $_SESSION['ipaddress'])
{
session_unset();
session_destroy();
}
if ($_SERVER['HTTP_USER_AGENT'] != $_SESSION['useragent'])
{
session_unset();
session_destroy();
}
if (time() > ($_SESSION['lastaccess'] + 3600))
{
session_unset();
session_destroy();
}
else{
$_SESSION['lastaccess'] = time();
}
?>
Last but not least, we will discuss CSRF. In this attack (also known as session riding), the attacker tricks your webserver into believing an authenticated user is issuing a request.
The idea is very simple. Some users log into your site and, while having their session open they get a notification of a very exciting opportunity to help some foreign prince get a considerable amount of money out of his country. All they need to do is click on a link to get more information.
What this link does is send a request to your website, which your server can’t distinguish from a legitimate one.
To prevent CSRF attacks, you need to make sure the request you’re getting comes from the user you believe. How do you do that?
One very common way of doing this is by creating an extra input that holds secret information. An example will help clarify.
Let’s use the blog site again. In the comment form you’d have this code:
<?php
session_start();
$token = $_SESSION["token"] = md5(session_id().time());
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Blog</title>
</head>
<body>
<form action="add_comment.php" method="post">
<input type="hidden" name="token" value="<?php echo $token; ?>"/>
<p>Comment: <input type="text" name="comment"/></p>
<p><input type="submit" value="Send"/></p>
</form>
</body>
</html>
And at the add_comment.php side:
<?php
session_start();
if (
!array_key_exists("token", $_SESSION) ||
!array_key_exists("token", $_POST) ||
$_SESSION["token"] !== $_POST["token"]
) {
die("Sorry, no CSRF for me, thanks!");
}
?>
The second most common vulnerability in PHP is called SQL Injection. In this scenario, the application allows an attacker to execute arbitrary code into its database. Let’s look at an example of how a successful attack will achieve.
Suppose your website is a blog that allows users to leave comments. You’d probably have a form like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Blog</title>
</head>
<body>
<form action="add_comment.php" method="post">
<p>Comment: <input type="text" name="comment"/></p>
<p><input type="submit" value="Send"/></p>
</form>
</body>
</html>
The file add_comment.php will look somewhat similar to:
<?php
try {
$db = new PDO('mysql:host=localhost;dbname=mydb');
} catch (PDOException $exception) {
die($exception->getMessage());
}
$sql = "INSERT INTO comments VALUES ('{$_POST['comment']}');";
try {
if ($db->exec($sql)) {
?><h1>Inserted!</h1><?php
} else {
?><h1>Not inserted 🙁 <?php echo print_r($db->errorInfo(), 1);?> </h1><?php
}
} catch (PDOException $exception) {
die($exception->getMessage());
}
?>
As in the previous example, nothing bad happens as long as the user behaves will expect (meaning, they enter a legitimate comment), but then again, we’re not here to talk about what regular users do, are we?
What happens when a malicious user fills his comment with a’); delete from comments; —?
The executed SQL is:
INSERT INTO comments VALUES ('a'); delete from comments; --');
The problem here is that the SQL sent to the database is not known when you’re writing the code since a part of it comes from external input: ($_POST[‘comments’]).
To prevent malicious code will inject into your legitimate query, you have two options:
Option 1 is possible when you know the possible values an input can take (for example, a dropdown list) or at least the type of data you will get from it (for example, an age). In this case, you can’t use this option since anyone can make any comment. You have to resort to option 2.
How do you prevent malicious code from being executed? Pretty much the same way you saw in preventing XSS attacks: you sanitize the input before using it. There are a few methods to do so using PHP.
The PHP Non-vulnerable code:
$sql = "INSERT INTO comments VALUES (: comment);”
try {
if ($db->exec($sql, [‘comment’ => $_POST[‘comment’]])) {
?><h1>Inserted!</h1><?php
} else {
?><h1>Not inserted 🙁 <?php echo print_r($db->errorInfo(), 1);?> </h1><?php
}
} catch (PDOException $exception) {
die($exception->getMessage());
}
?>
The difference is that now, before executing the sentence, PDO takes care of escaping the parameter for you. This means adding a \ before every ‘which means the resulting.
SQL sentence is:
INSERT INTO comments VALUES ('a\'); delete from comments; --');
This results in a rather weird comment but nothing else.
Remote File Inclusion or RFI occurs when a PHP application takes user input and passes it to a function that will design to load a file. If this file is a URL, the function may load PHP code from another website that an attacker specifies which will then execute on your website. The inclusion of a remote file in a URL is known as Remote File Inclusion or RFI.
If the file an attacker passes is local, the application may be output the contents of that file to the screen. This is commonly how an attacker gains access to a WordPress website’s wp-config.php file. This is known as Local File Inclusion or LFI.
The functions that can be susceptible to RFI and LFI in PHP are: include, include_once, open, file_get_contents, require, and require_once.
All of these functions load PHP code or content from a source that the developer specifies. If the PHP installation on the website will configure insecurely, an attacker can load a sensitive file as PHP code or content, or a remote file that lets them gain access to your website.
Most modern PHP installations protect against RFI attacks that load remote URLs by limiting where files will include from. However, it is quite common for PHP developers to accidentally write code that allows an attacker to gain access to a local file like wp-config.php. For this reason, Remote File Inclusion vulnerabilities are rare these days, while Local File Inclusion vulnerabilities are quite common.
This article has given you an understanding of the most common types of PHP vulnerabilities and how they are created. As a responsible Offensive360, you will be monitoring security mailing lists and will alert you to new vulnerabilities as they emerge. Most of the vulnerabilities you will see will be one of the above types. With the new knowledge you will gain, you will better understand the severity of these vulnerabilities, you will be better equipped to ask developers questions about the vulnerability and you will have a good knowledge of how the vulnerability may be exploited on your website before you patch it.