OWASP Backend Security Project PHP Security Programming

= Overview =

Example 1
Here follows a tipical Login Forms to authenticate user. Credentials are retrieved on a backend Database by using connection parameters stored in a .inc file.

auth.php

<?php include('./db.inc');

function sAuthenticateUser($username, $password){ $authenticatedUserName=""; if ($link = iMysqlConnect) {

$query = "SELECT username FROM users"; $query .=               " WHERE username = '".$username."'"; $query .=               " AND   password = md5('".$password."')"; $result = mysql_query($query);

if ($result) { if ($row = mysql_fetch_row($result)) { $authenticatedUserName = $row[0]; }   }  }

return $authenticatedUserName;

}

if ($sUserName = sAuthenticateUser($_POST["username"], $_POST["password"])) {

/* successfull authentication code goes here */ ...  ... } else {

/* unsuccessfull authentication code goes here */ ...  ... }

?>

db.inc



The above example has two vulnerability:


 * Authentication Bypass
 * by exploiting a SQL Injection vulnerability Authentication you can authenticate as :
 * username ' OR 1=1 #
 * password anything
 * Information Disclosure
 * an attacker may retrieve db.inc on unproper configured WEB Server

Example 2
The following sample code cames from a online book catalog.

getbook.php

function aGetBookEntry($id) { $aBookEntry = NULL; $link = iMysqlConnect;

$query = "SELECT * FROM books WHERE id = $id"; $result = mysql_query($query);

if ($result) { if ($row = mysql_fetch_array($result)) { $aBookEntry = $row; } }  return $aBookEntry; }

.... $id = $_GET['id']; $aBookEntry = aGetBookEntry($id);

/* Display retrieved book information */ ... ...

The above example is vulnerable to Blind SQL Injection attack. An attacker exploiting this vulnerability may backup on his laptop your Database content, or even worst can spawn a remote shell into it.

= Description =

Escaping Quotes
magic_quotes_gpc escapes quotes from HTTP Request by examing both GET/POST data and Cookie value. The truth is that any other data in HTTP Request isn't escaped and an evil user may attempt to exploit a SQL Injection vulnerability on other HTTP Request data such as User-Agent value. Another drawback is that while it performs well on MySQL (as example) it doesn't works with Microsoft SQL Server where single quote should be escaped with   '''

magic_quotes_gpc should never be used since:


 * only GET/POST/COOKIE data are escaped in served HTTP Request
 * it doesn't guarantee compatibility between different DBMS

Since every application should be portable across WEB Servers we want to rollaback from magic_quotes_gpc each time a php script is running on WEB Server:

function magic_strip_slashes { if (get_magic_quotes) {

// GET if (is_array($_GET)) { foreach ($_GET as $key => $value) { $_GET[$key] = stripslashes($value); }      }

// POST if (is_array($_POST)) { foreach ($_GET as $key => $value) { $_POST[$key] = stripslashes($value); }      }

// COOKIE if (is_array($_COOKIE)) { foreach ($_GET as $key => $value) { $_COOKIE[$key] = stripslashes($value); }      }    } }

and use a DBMS related function to escape quotes such as: function sEscapeString($sDatabase, $sQuery) { $sResult=NULL; switch ($sDatabase) { case "mysql": $sResult = mysql_escape_string($sQuery); break; case "postgresql": $sResult = pg_escape_string($sQuery); break;
 * MySQL: mysql_real_escape_string
 * PostgreSQL: pg_escape_string

case "mssql": $sResult = str_replace("'", "''",$sQuery); break;

case "oracle": $sResult = str_replace("'", "''",$sQuery); break; }

return $sResult; } }

Both Oracle and Microsoft SQL Server connectors doesn't have a real escape_string function so you need to escape every  '  with    by using your own escapeing function or addslashes''.

With properly quotes escaping we can prevent Authentication Bypass vulnerability in Example 1:

auth.php

<?php include('./db.inc');

function sAuthenticateUser($username, $password){ $authenticatedUserName=""; if ($link = iMysqlConnect) {

$query = "SELECT username FROM users"; $query .=               " WHERE username = '".$username."'"; $query .=               " AND   password = md5('".$password."')";

/* escape quotes */ $result = sEscapeString("mysql", $query);

if ($result) { if ($row = mysql_fetch_row($result)) { $authenticatedUserName = $row[0]; }   }  }

return $authenticatedUserName; }

/* start by rollback magic_quotes_gpc action (if any) */

magic_strip_slashes;

if ($sUserName = sAuthenticateUser($_POST["username"], $_POST["password"])) {

/* successfull authentication code goes here */ ...  ... } else {

/* unsuccessfull authentication code goes here */ ...  ... }

Defeating Automated Tools
= References =