This site is the archived OWASP Foundation Wiki and is no longer accepting Account Requests.
To view the new OWASP Foundation website, please visit https://owasp.org

Difference between revisions of "OWASP Backend Security Project PHP Preventing SQL Injection"

From OWASP
Jump to: navigation, search
(Login Form)
Line 1: Line 1:
 
= Examples =
 
= Examples =
 +
To better understand how to secure code a PHP application some examples of
 +
vulnerable code is provided in this paragraph. 
  
 
== Login Form ==
 
== Login Form ==
 +
On this example we're going to see a tipical Login Form. On our example WEB SITE user need to supply a username/password pair in order to be authenticated.
 +
                                                       
 +
                                                                                                                                                   
 +
Here follows the authentcation form:                               
  
 +
[[Image:Owasp_bsp_php_1.jpg]]
 +
 +
Such a login page well call ''login.php'' with supplied user credentials.
 +
 +
  <nowiki>
 +
<?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;
  
 +
}
  
[[Image:Owasp_bsp_php_1.jpg]]
+
if ($sUserName = sAuthenticateUser($_POST["username"],
 +
  $_POST["password"])) {
 +
  echo "Wellcome ".$sUserName;
 +
} else {
 +
  die('Unauthorized Access');
 +
}
 +
 
 +
?>
 +
</nowiki>
 +
 
 +
'''db.inc:'''
 +
  <nowiki>
 +
 
 +
<?php
 +
 
 +
define('DB_HOST',    "localhost");
 +
define('DB_USERNAME', "user");
 +
define('DB_PASSWORD', "password");
 +
define('DB_DATABASE', "owasp");
 +
 
 +
 
 +
function iMysqlConnect(){
 +
  $link = mysql_connect(DB_HOST,
 +
    DB_USERNAME,
 +
DB_PASSWORD);
 +
 
 +
  if ($link && mysql_select_db(DB_DATABASE))
 +
    return $link;
 +
  return FALSE;
 +
}
 +
 
 +
?></nowiki>
 +
 
 +
 
 +
=== Source Code Exposure Vulnerability ===
 +
 
 +
As you can see ''login.php'' include ''db.inc'' to use ''iMysqlConnect'' whose aims is to
 +
return a resource link to backend MySQL engine. Since ''.inc'' files are not rendered
 +
by WEB Server through the ''PHP Module'' an evil user may retrieve it's content as shown
 +
in the following images:
  
 
[[Image:Owasp_bsp_php_2.jpg]]
 
[[Image:Owasp_bsp_php_2.jpg]]
 +
 +
 +
=== SQL Injection Vulnerability ===
 +
 +
 +
On our examples user credentials are passed to ''login.php'' wich in turns call the following
 +
PHP function:
 +
 +
 +
<nowiki>
 +
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;
 +
 +
}
 +
</nowiki>
 +
 +
When user ''larry'' authenticate the following SQL Query it's executed:
 +
* SELECT username FROM users WHERE username='larry' AND password=md5('larry')
 +
 +
Breaking such an authentication schema is quite simple, since we need to :
 +
 +
* truncate backend authentication query
 +
* injection a boolean expression to override WHERE clausole
 +
 +
 +
* On MySQL ''#'' is used as a comment character then whatever follows is discarderd from the engine.
 +
* Since our WHERE clausole is something like ''expr AND expr'' we can override it by adding an identity expresssion (E.g.: OR 1=1)
 +
 +
 +
It follows that authentication can be bypassed by supplying the following credentials:
 +
* username: ''' OR 1=1#''
 +
* password: ''password''
 +
 +
* username: ''username''
 +
* password: ''') OR 1=1 #''
 +
 +
 +
=== User Enumeration ===
 +
 +
We learned on previous section that an evil user can bypass authentication schema by supplying
 +
the following credentials:
 +
 +
* username: '''OR 1=1#''
 +
* password: ''password''
 +
 +
Let'say again what does ''sAuthenticateUser'' performs:
 +
 +
<nowiki>
 +
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;
 +
 +
}
 +
</nowiki>
 +
 +
 +
As you can see $query will contains the following SQL statement:
 +
* SELECT username FROM users WHERE username = '' OR 1=1#' AND  password = md5('password')
 +
 +
wich in turns will be executed in:
 +
 +
<nowiki>
 +
  $result = mysql_query($query); 
 +
</nowiki>
 +
 +
By adding a ''LIMIT'' operator after the ''OR'' clausole we can index every valid user with the following
 +
queries:
 +
 +
* SELECT username FROM users WHERE username = '' OR 1=1 LIMIT 0,1
 +
* SELECT username FROM users WHERE username = '' OR 1=1 LIMIT 1,1
 +
* SELECT username FROM users WHERE username = '' OR 1=1 LIMIT 2,1
 +
* ''.... and so on''
 +
 +
We can accomplish the above task by supplying the following ''username/password'' pairs:
 +
* username: ''' OR 1=1 LIMIT 0,1#''
 +
* password: ''password''
 +
 +
 +
* username: ''' OR 1=1 LIMIT 1,1#''
 +
* password: ''password''
 +
 +
 +
* username: ''' OR 1=1 LIMIT 2,1#''
 +
* password: ''password''
 +
 +
  
 
== Online Catalog ==
 
== Online Catalog ==

Revision as of 17:05, 18 May 2008

Examples

To better understand how to secure code a PHP application some examples of vulnerable code is provided in this paragraph.

Login Form

On this example we're going to see a tipical Login Form. On our example WEB SITE user need to supply a username/password pair in order to be authenticated.


Here follows the authentcation form:

Owasp bsp php 1.jpg

Such a login page well call login.php with supplied user credentials.

 
<?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"])) {
  echo "Wellcome ".$sUserName;
 } else {
  die('Unauthorized Access');
 }

?>

db.inc:

 

<?php

define('DB_HOST',     "localhost");
define('DB_USERNAME', "user");
define('DB_PASSWORD', "password");
define('DB_DATABASE', "owasp");


function iMysqlConnect(){
  $link = mysql_connect(DB_HOST,
  	  DB_USERNAME,
			DB_PASSWORD);

  if ($link && mysql_select_db(DB_DATABASE))
    return $link;
  return FALSE;
}

?>


Source Code Exposure Vulnerability

As you can see login.php include db.inc to use iMysqlConnect whose aims is to return a resource link to backend MySQL engine. Since .inc files are not rendered by WEB Server through the PHP Module an evil user may retrieve it's content as shown in the following images:

Owasp bsp php 2.jpg


SQL Injection Vulnerability

On our examples user credentials are passed to login.php wich in turns call the following PHP function:


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;

}

When user larry authenticate the following SQL Query it's executed:

  • SELECT username FROM users WHERE username='larry' AND password=md5('larry')

Breaking such an authentication schema is quite simple, since we need to :

  • truncate backend authentication query
  • injection a boolean expression to override WHERE clausole


  • On MySQL # is used as a comment character then whatever follows is discarderd from the engine.
  • Since our WHERE clausole is something like expr AND expr we can override it by adding an identity expresssion (E.g.: OR 1=1)


It follows that authentication can be bypassed by supplying the following credentials:

  • username: ' OR 1=1#
  • password: password
  • username: username
  • password: ') OR 1=1 #


User Enumeration

We learned on previous section that an evil user can bypass authentication schema by supplying the following credentials:

  • username: 'OR 1=1#
  • password: password

Let'say again what does sAuthenticateUser performs:

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;

}


As you can see $query will contains the following SQL statement:

  • SELECT username FROM users WHERE username = OR 1=1#' AND password = md5('password')

wich in turns will be executed in:

  $result = mysql_query($query);  

By adding a LIMIT operator after the OR clausole we can index every valid user with the following queries:

  • SELECT username FROM users WHERE username = OR 1=1 LIMIT 0,1
  • SELECT username FROM users WHERE username = OR 1=1 LIMIT 1,1
  • SELECT username FROM users WHERE username = OR 1=1 LIMIT 2,1
  • .... and so on

We can accomplish the above task by supplying the following username/password pairs:

  • username: ' OR 1=1 LIMIT 0,1#
  • password: password


  • username: ' OR 1=1 LIMIT 1,1#
  • password: password


  • username: ' OR 1=1 LIMIT 2,1#
  • password: password


Online Catalog

Message Board

PHP Application Security strategies

Hiding DBMS connection strings

Prepared Statement

Data Validation

Security in Depth

Defeating Automated Tools