Avoiding PHP Vulerabilities
Securing PHP - How to avoid basic exploits and vulnerabilities.
Hey all, time for the next tutorial now... and this time, I am gonna give you some ideas for writing secure code for general coding flaws in PHP. Most of the time, the programmers forget to sanitize the user input in their PHP code and hence, the code becomes vulnerable to some of the common exploits like file inclusion vulnerabilities, SQL injection, XSS and others... So I am here to give you ideas on preventing these simple vulnerabilities in your PHP code...
File Inclusion:
File inclusion vulnerabilities, like RFI (remote) and LFI (local) are exploited by including another file (other than intended by programmer) and this is damn devastating as we can completely RM the box if we escalate privilege with PoC (proof of concept) exploits... Anyway let me show the vulnerable code:
<?php
$page = $_GET["page"];
# Checks if the variable $page is set or not
if (isset($page)) {
# Includes the page without checking if it is legitimate...
include(page);
}
?>
I've seen many programmers writing the same code, especially PHP coders from Nepal and it leads to unexpected results... So any malicious user can include some evil files to r00t the box and you are own3d (Admin note: see tutorial #2, RFI)
Also many programmers think that they can patch this vuln with the following snippet (based on real example from one of the ISPs)
<?php
$venpage = $_GET["page"];
$venpage = $venpage . ".php";
if (isset($venpage)) {
include($venpage);
}
?>
This seems to work... aha but still it has got a hole... NULL BYTES - %00 - ? Oh hacked but I secured it... Did you??? No, you didn't!
So let me talk about securing it... The switch is the perfect and simplest method to secure this whole code...
<?php
$page = $_GET["page"];
# Check if there's page variable set or not
if (isset($page)) {
switch($page) {
case "info":
include("info.php");
break;
case "about":
include("about.php");
break;
default:
include("index.php");
break;
}
}
?>
The above written code is simple yet secured... the switch statement predefines all the set pages so unlisted pages can't be added by the hacker. So why not use it... Damn perfect...
Another method though I don't use it much is:
<?php
//error_reporting(E_ALL);
if (isset($_GET["page"])) {
$page = $_GET["page"];
$page = preg_replace("/[^a-z]+/i", "", $page);
include $page . ".php";
} else {
echo "No page set";
}
?>
This also should work fine though as already stated I don't use this one... It's a regular expression method (only allows the characters a-z from the page variable)...
SQL Injection:
SQL injections are one of the most prevalent web vulns on websites and they can be very harmful especially for the commercial sites... But still many sites still remain vulnerable to the SQL injection. And again the problem is again the lack of sanitization of GET/POST variables or any other inputs from users... To avoid SQL injection, you need to be as hard as you can. Don't allow any other data types where you assume to be integer types. Don't allow something that is not what you wanted to be accepted by your code. Be as strict as you can for the data types.
Now let me show you the simplest form of the vulnerability:
<?php
// Configurations for mysql connection
$host = "localhost";
$user = "root";
$pass = "w000000t";
$db = "db_shop";
// Connecting to mysql
mysql_connect($host, $user, $pass);
mysql_select_db($db);
$uid = $_GET["uid"];
if (isset($uid)) {
$query = mysql_query("SELECT * FROM `profile` WHERE `uid` = $uid");
if ($query) {
while($profile = mysql_fetch_array($query)) {
// Display or do something here
}
}
}
?>
You can see that this takes uid from GET i.e. from user and works accordingly. Seems fine and most of the site visitors won't know about it. But what if someone elite visits the site. He/She will test the GET variable and change the uid value.
The query runs and runs without any filtering mechanism. And if the malicious runs the SQL query, he can do anything to the database. So what's the solution for this? Simply, type checking. You won't expect uid to be anything other than integer type. So why not tell PHP that the uid must be integer...
<?php
// Configurations for mysql connection
$host = "localhost";
$user = "root";
$pass = "w000000t";
$db = "db_shop";
// Connecting to mysql
mysql_connect($host, $user, $pass);
mysql_select_db($db);
$uid = (int) $_GET["uid"]; // You say that uid must be integer...
if (isset($uid)) {
$query = mysql_query("SELECT * FROM `profile` WHERE `uid` = $uid");
if ($query) {
while($profile = mysql_fetch_array($query)) {
// Display or do something here
}
}
}
?>
So this should now be secure...
Moreover, there are several functions in PHP for various kinds of validation and escaping like mysql_real_escape_string(), htmlentities(), strip_tags(), etc. And there are different other ones to validate the datatypes like is_numeric() and settype(). Explore them and be secured.
With this, the tutorial for secure coding in PHP has ended... I might extend this to validate uploads also when I have time but till then, bye and be safe.
Thanks to t0mmy9 and sOwL for always being friendly and helpful... You r0ck...By: sam207
Tutorial by sam207