For newbies or more advanced PHP & MySQL programmers, knowing how to properly secure a script is one of the most important things you can do. Note: To understand the following information, you must understand PHP programming to some degree. A Beginning PHP programmer should be able to understand this as well though.
What can happen if I do not secure a script properly?
If a script is not secure, it can allow for other advanced users to gain access to your non-web accessible pages and directories or even the entire machine.
Why does PHP / MySQL have vulnerabilities?
PHP and MySQL are both secure, the error lies in how you code your site. Since PHP is a programming language you have control over exactly what you want to happen. This being said, if you do not understand how an attack is performed on your site then you won't understand how and why you can fix it.
Let's Get Start!
The primary form of attack on a website is the easiest to defend against. It is known as a "MySQL Injection Attack". While MySQL is the easiest to gain access to if unprotected, there are other PHP only injection attacks that may allow an attacker to get to administrative parts of the website, run their own PHP code giving them access to your system, or worse (delete all your files)...
All injection attacks work the same way and can be protected against in the same way.
The following is our example script without MySQL, this will be named index.php:
<?php
// file name index.php
$name = $_GET['name'];
if(!empty($name)){
echo "Hello $name!";
}else{
echo "What is your name:
<form action=\"index.php\" method=\"get\">
<input type=\"text\" name=\"name\" value=\"\" />
<input type=\"submit\" name=\"submit\" value=\"Click The Button!\" />
</form>";
}
?>
This script will ask the user for their name with an input box. When they enter their information into the input box and submit the form, lets say their name is "John", the URL changes to:
index.php?name=John
The script then pulls the $_GET['name'] (which is equal to "John") and reassigns it to $name. Then we logically check to see if the $name variable is empty or not.
If $name is not empty then we say , "Hello $name"....or in this case it would be "Hello John".
Now, an injection attack is where we use the $_GET or $_POST variables to exploit a script. $_GET and $_POST are special - they are used so that a user can interact with a website. They are the only variables that take user input and bring it into the script to be worked with.
An example of an injection attack that uses javacsript would be to submit javascript code in the name field. When the page refreshes the javascript would be executed and the user might be redirected to a different site or something similar. In our example script, this would only work if multiple users could visit the page with the javascript code passed in the URL....other than that this script is pretty much OK because of how simple it is.
--
Lets make an example that would allow someone to execute a MySQL Injection attack:
<?php
// file name index.php
// enable sessions
session_start();
// MySQL Settings
$mysql['host'] = "localhost";
$mysql['user'] = "user";
$mysql['pass'] = "pass";
$mysql['db'] = "temp";
// Connect to mysql server
$conn = @mysql_connect($mysql['host'], $mysql['user'], $mysql['pass']) or die ('Error connecting to mysql');
// Choose what database to work from.
mysql_select_db($mysql['db']) or die("The MySQL table selected does not exist or you do not have permissions to access it.");
// create the form in a function for easy recall:
function form(){
global $username;
echo "<form action=\"\ method=\"\">";
echo "<input type=\"text\" name=\"username\" value=\"$username\" />";
echo "<input type=\"text\" name=\"password\" value=\"\" />";
echo "<input type=\"submit\" name=\"submit\" value=\"Login\" />";
echo "</form>";
}
$username = $_POST['username'];
$password = $_POST['password'];
if(isset($_POST['submit'])){
$gu = mysql_query("SELECT * FROM users WHERE user='$username' && pass='$password' LIMIT 1");
$cgu = mysql_num_rows($gu);
if($cgu > 0){
$agu = mysql_fetch_array($gu);
extract($agu);
// Extraxt will extract the column names in MySQL directly into variables.
// Since $agu is an array, we can extract it.
$_SESSION['userid'] = $userid;
// redirect user
header("Location: http://domain.com/file.php");
}else{
echo "Sorry, but the username and password combination was incorrect. Please try again!<br />";
form();
}
}else{
form();
}
?>
If you were to submit the following text into the username and password columns, you could log into the site without proper authentication:
' or 1=1--
The reason is that the SQL Query changes to this:
SELECT * FROM users WHERE user='' or 1=1--
The 2 dashes tell MySQL that you want it to ignore everything after the dashes. So you typically end up selecting the first user in the database, which is usually an administrative account.
MySQL attacks aren't always just "' or 1=1--", they can pass an SQL statement to save all the user accouts in the database into a txt file to a web accessible location so that they can spam your users.
They could also upload a file, such as another PHP script. There is a file called c99.php, which is a single PHP file that allows a user access to your system - Not just web accessible locations but most of the time, your ENTIRE webserver! This means that they could delete operating system files, other users accounts...they could, in an extreme situation, gain access to your entire local network and allow themselves access to every computer on the network...Once they get into your site there is no stopping them until you fix the issue.
So how do you fix an injection attack?
I will give you a few PHP functions that should be applied to all $_GET or $_POST variables:
mysql_real_escape_string(); - If you are connected to a mysql server, this function will send the variable passed to it to the mysql server and the mysql server will then properly escape the string so that it can not be used for an injection attack.
addslashes(); - If your script does not use mysql you can alternatively use addslashes(); to escape apostrophes and quotes.
htmlentities(); - This will turn certain chars into their html entity equivalent. (Because Qondio will not let me place these codes in with out converting them, I will ask you to visit http://www.w3.org/MarkUp/html3/latin1.html for more information.
trim(); - This will remove any extra trailing spaces.
I use these in combination with each other to protect against MySQL and Javascript injection type attacks.
In our previous script all we would need to do is change that post (or get) variables as such:
<?php
$username = mysql_real_escape_string(htmlentities(trim($_POST['username'])));
$password = mysql_real_escape_string(htmlentities(trim($_POST['password'])));
?>
I got tired of doing all this, so at the top of every script I have the following code so I don't have to repeatedly write it out:
<?php
foreach ($_POST as $k => $v){
$_POST[$k] = mysql_real_escape_string(htmlentities(trim($v)));
}
foreach ($_GET as $k => $v){
$_GET[$k] = mysql_real_escape_string(htmlentities(trim($v)));
}
?>
I hope this helps you understand how an injection attack is executed and how you can prevent it from happening to you. More information and PHP/MySQL code samples can be found at http://iluvjohn.com/