Security concerns in PHP development

  • 2020-03-31 21:04:56
  • OfStack

As PHP programmers, especially the novice, always know too little about the sinister Internet, for external intrusion is often no strategy, they simply do not know how the hacker is to invade, submit intrusion, upload vulnerability, SQL injection, cross-script attacks, and so on. As the most basic precaution you need to pay attention to your external submissions, do a good job with the first side of the security mechanism handling the firewall.

Rule 1: never trust external data or input

The first thing you must realize about Web application security is that you should not trust external data. Outside data includes any data that is not entered directly by the programmer in PHP code. Any data from any other source (such as a GET variable, form POST, database, configuration file, session variable, or Cookie) is not trusted until steps are taken to ensure security.

For example, the following data elements can be considered safe because they are set in PHP.

Listing 1. Spotless security code
 
$myUsername =  ' tmyer'; 
$arrayarrayUsers = array( ' tmyer',  ' tom',  ' tommy'); 
define( " GREETING " ,  ' Hello there' . $myUsername); 
?> 

However, the following data elements are flawed.

Listing 2. Unsafe, flawed code
 
$myUsername = $_POST['username']; //tainted! 
$arrayarrayUsers = array($myUsername,  ' tom',  ' tommy'); //tainted! 
define( " GREETING " ,  ' hello there' . $myUsername); //tainted! 
?> 

Why is the first variable $myUsername flawed? Because it comes directly from the form POST. The user can enter any string in this input field, including malicious commands to clean up files or run previously uploaded files. You might ask, "can't you avoid this danger by using client-side (Javascr pt) form validation scripts that accept only the letter a-z?" Yes, this is always a good step, but as you'll see later, anyone can download any form to their machine, modify it, and then resubmit whatever they need.

The solution is simple: you must run the cleanup code on $_POST['username']. If you don't, you might contaminate these objects any other time you use $myUsername, such as in an array or constant. An easy way to clean up user input is to use regular expressions to process it. In this example, you only want to accept letters. It might also be a good idea to limit strings to a certain number of characters, or to require all letters to be lowercase.

Listing 3. Making user input secure
 
$myUsername = cleanInput($_POST['username']); //clean! 
$arrayarrayUsers = array($myUsername,  ' tom',  ' tommy'); //clean! 
define( " GREETING " ,  ' hello there' . $myUsername); //clean! 
function cleanInput($input){ $clean = strtolower($input); 
$clean = preg_replace( " /[^a-z]/ " ,  "" , $clean); 
$clean = substr($clean,0,12);return $clean; 
} 
?> 

Rule 2: disable PHP Settings that make security difficult to enforce

Having learned that you can't trust user input, you should also know that you shouldn't trust the way PHP is configured on your machine. For example, be sure to disable register_globals. If register_globals is enabled, you might do something careless, such as using $variable to replace a GET or POST string with the same name. By disabling this setting, PHP forces you to refer to the correct variable in the correct namespace. To use the variable from the form POST, you should refer to $_POST['variable']. This way you won't mistake this particular variable for a cookie, session, or GET variable.

Rule 3: if you can't understand it, you can't protect it

Some developers use strange syntax, or organize statements in a compact way that forms short, cryptic code. This approach may be efficient, but if you don't understand what your code is doing, you can't decide how to protect it. For example, which of the following two pieces of code do you like?

Listing 4. Making your code easy to protect
 
//obfuscated code 
$input = (isset($_POST['username']) ? $_POST['username']: " ); 
//unobfuscated code 
$input =  " ; 
if (isset($_POST['username'])){ 
$input = $_POST['username']; 
}else{ 
$input =  " ; 
} 

In the second, cleaner snippet, it's easy to see that $input is flawed and needs to be cleaned up before it can be handled safely.

Rule 4: "defense in depth" is the new magic

This tutorial USES examples to show how to protect online forms, while taking the necessary steps in the PHP code that handles them. Also, even if you use PHP regex to ensure that the GET variable is entirely numeric, you can still take steps to ensure that SQL queries use escaped user input. Defense in depth is not just a good idea, it ensures you don't get into serious trouble. Now that you've covered the ground rules, let's look at the first threat: the SQL injection attack.

In pieces prevent SQL injection attacks

In an SQL injection attack, users add information to a database query by manipulating a form or GET query string. For example, suppose you have a simple login database. Each record in this database has a username field and a password field. Build a login form so that users can log in.
 
<html> 
<head> 
<title>Login</title> 
</head> 
<body> 
<form action= " verify.php "  method= " post " > 
<p><label for='user'>Username</label> 
<input type='text' name='user' id='user'/> 
</p> <p><label for='pw'>Password</label> 
<input type='password' name='pw' id='pw'/> 
</p> <p><input type='submit' value='login'/></p> 
</form> 
</body> 
</html> 

This form takes the user's username and password and submits the user input to a file called verify.php. In this file, PHP processes the data from the login form, as shown below:

Listing 5. Unsafe PHP form processing code
 
<?php 
$okay = 0; 
$username = $_POST['user']; 
$pw = $_POST['pw']; 
$sql =  " select count(*) as ctr from users where username=' 
 " .$username. "'  and password=' " . $pw. "'  limit 1 " ; 
$result = MySQL_query($sql); 
while ($data = mysql_fetch_object($result)){ 
if ($data->ctr == 1){ 
//they're okay to enter The application! 
$okay = 1; 
} 
} 
if ($okay){ 
$_SESSION['loginokay'] = true; 
header( " index.php " ); 
}else{ 
header( " login.php " ); 
} 
?> 

This code looks fine, right? Hundreds (if not thousands) of PHP/MySQL sites around the world use this code. What's wrong with it? Well, remember that "user input cannot be trusted". There is no escaping of any information from the user, thus leaving the application vulnerable. In particular, any type of SQL injection attack can occur. For example, if the user enters foo as the username and 'or '1' ='1 as the password, it will actually pass the following string to PHP and then pass the query to MySQL:
 
<?php 
$sql =  " select count(*) as ctr from users where username= 
'foo' and password= "  or '1 ' ='1 '  limit 1 " ; 
?> 

This query always returns a count of 1, so PHP allows access. By injecting some malicious SQL at the end of the password string, the hacker can impersonate a legitimate user. The solution to this problem is to use PHP's built-in mysql_real_escape_string() function as a wrapper for any user input. This function escapes characters in a string, makes it impossible to pass special characters such as apostrophes and lets MySQL operate on them. Listing 7 shows the code with escape handling.

Listing 7 shows the code with escape handling
 
<?php 
$okay = 0; 
$username = $_POST['user']; 
$pw = $_POST['pw']; 
$sql = "select count(*) as ctr from users where username='".mysql_real_ 
_string($username)."' and password='". mysql_real_escape_string($pw)."' 
limit 1"; 
$result = mysql_query($sql); 
while ($data = mysql_fetch_object($result)){ 
if ($data->ctr == 1){ //they're okay to enter the 
application! 
$okay = 1; 
} 
} 
if ($okay){ 
$_SESSION['loginokay'] = true; 
header("index.php"); 
} 
else{ 
header("login.php"); 
} 
?> 

Using mysql_real_escape_string() as a wrapper around user input prevents any malicious SQL injection from user input. If the user attempts to pass the malformed password through SQL injection, the following query is passed to the database:
 
select count(*) as ctr from users where username='foo' and password= 
'' or '1'='1 '  limit 1 "  

Related articles: