Ready to Start Your Career?

A Cautionary Tale about PHP Secure Coding Techniques

V 's profile image

By: V

July 15, 2016

rope-lights-in-non-motion-cybraryCommon Methods to Secure PHP Application Input DataPHP is one of the most versatile languages in recent history for web applications, sites and services. But, this versatility means it also has large vectors for exploitation and attack, which has led many to scrutinize the language as flawed or insecure by design.However, this is a belief created and fostered by those who do not know how to secure it properly, and who do not know how to write secure code. Most times, they don't even realize the most simple pieces of coding can be dangerous. I have seen cases where supposed PHP Experts were baffled as to how a piece of code could be vulnerable, and then had to educate them that the simplest omission or mistake can leave a wide open door to attackers.PHP was built to be highly improvisational. You can use its functions and extensions in an unlimited and infinite number of combinations to achieve everything from:
  • a simple math equation
  • controlling heavy machinery thousands of miles away
  • serving a simple tax form that auto calculates your yearly dues
  • creating custom encryption and encoding tools.
PHP can be setup to hook network sockets, Linux pipes, and controlhardware physically attached to its machine, or even remotely on a machine far far away.Below, I'll cover a number of examples of code where user input is received, and explain the different ways of handling it, sterilizing it and limiting what input it will accept.REMEMBER: the most secure code will be the code that ONLY ACCEPTS valid input, and REJECTS or DROPS any bad input. Don't ever let bad input reach your functional code - catch it before it gets there.The items this article will focus around:
  • GET
  • POST
These are the most widely used and exploited attack vectors. Data from these sources should NEVER be trusted at face value or allowed to be used in functional code until it's  fully sterilized and checked for nasty things.SQLMAP is one of the more prolific tools out in the wild for finding and exploiting the security flaws in the aforementioned user input items. It can find a hole, fingerprint what that hole is vulnerable to and generate its own exploit code (almost like a skeleton key). Then, depending on the type of hole, allow database downloads, source code downloads, uploads of more dangerous exploits, and even root access and hijacking of the entire server in extreme cases. And, it can do all that in a matter of minutes in some cases. The Story:Xander makes a basic whois tool to put on his website to allow users to lookup the owners of websites from his website. But, his server has been acting strange ever since.His code was this:<?php$website = $_GET['website'];$cmd = "whois $website";passthru($cmd);?>Congratulations Xander, you have exposed direct shell access to attackers. In his code, all they need to use it is a simple semi-colon. IF you have PHP running as root, the attacker now owns your server. If not, the attacker will simply upload a file to escalate himself to root.Xander would probably respond, "That's impossible - my code doesn't allow all of that!!!!"But, that's when we must educate Xander and show him how simple it is. The browser call to find out what user PHP is running as would be:

file.php?website=a ; whoami

Notice that Linux commands can be strung together with semi-colons. The output will show the error from the incomplete whois query, but then show that you are running as most likely Apache or NGINX. Xander would then argue, "Well, but they can't upload a file with that, so I'm safe!"We would then have to show Xander how easy that is.

file.php?website=a ; (echo attack code here) >

Xander would then freak, when he saw the sh file appear right in the same folder as his. We would advise Xander, this is nearly as simple to fix as it was to mess up with just a line of code.  And show Xander how to limit input to only what is absolutely needed for the script to work.  And show him the following re-written version of his PHP code:<?php$website = preg_replace("/[^A-Za-z0-9.-]/", '', $_GET['website']);$cmd = "whois $website";passthru($cmd);?>This simple change didn't even add a line, but it filters the input stripping anything that isn't a letter, number, dash or period - which are the only things domain names contain. Xander then informs us his homepage has code that saves the useragent of visitors to MySQL and that recently he has been getting lots of MySQL syntax errors in the logs from that file, but he's certain that his SQL syntax is perfect. He shows you his code:<?phpinclude("dbconfig.php");$useragent = $_SERVER['HTTP_USER_AGENT'];$ip = $_SERVER['REMOTE_ADDR'];$time = time();$counter = mysql_query("INSERT INTO useragents SET ip = '$ip', time = '$time', useragent = '$useragent' ");?> Xander then says, "There are no inputs from the user, so how can this be exploited?"We then show Xander, how SQLMAP will alter its own useragent to exploit any code that uses the useragent data, just as it does with GET and POST, and we dump a copy of Xanders entire database to his amazement and horror. We then explain that this to is so simple to fix, and NEVER, NEVER, NEVER trust any data coming in from users, no matter where it comes from. We show Xander to fix this, while preserving the format and content as well. we would use thefollowing code:<?phpinclude("dbconfig.php");$useragent = base64_encode($_SERVER['HTTP_USER_AGENT']);$ip = $_SERVER['REMOTE_ADDR'];$time = time();$counter = mysql_query("INSERT INTO useragents SET ip = '$ip', time = '$time', useragent = '$useragent' ");?> We then advise Xander that he must use the appropriate base64_decode function on any pages that display the useragent data.We add that, a good function to put into his main config file at the very top, would be:if(strripos($_SERVER['REQUEST_URI'], "UNION") !== false) {if(strripos($_SERVER['REQUEST_URI'], "SELECT") !== false) {die;}} We explain that some of the most common SQL Injections involve the combination of these two words in the URI, and that this function will terminate execution if they are detected. And furthermore, if he builds the rest of his pages to not use either word in the URIs, he can de-nest the second IF statement from the first and achieve better coverage of possible attempts. These two statements are unnecessary if all of the code is perfectly secured in thefirst place.Xander explains that he never thought about how his code could be used in such strange and unusual ways by an attacker, and that we have opened his eyes to how you must limit the ways the code is able to function, in order to protect it.We add to Xander that he should employ variations of these methods for filtering and protecting not only his GET and Useragent inputs, but his POST and COOKIE inputs as well. And, advise that, just because data is stored in a cookie for your site, doesn't mean the COOKIE isn't forged. Thanks and I hope this tale was helpful.
Schedule Demo