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 Security Programming"
(→References) |
|||
Line 346: | Line 346: | ||
* Ilia Alshanetsky : "architect's Guide to PHP Security" - http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf | * Ilia Alshanetsky : "architect's Guide to PHP Security" - http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf | ||
* OWASP : "OWASP Guide Project" - http://www.owasp.org/index.php/OWASP_Guide_Project | * OWASP : "OWASP Guide Project" - http://www.owasp.org/index.php/OWASP_Guide_Project | ||
+ | * PHP : "MySQL Improved Extension" - http://it2.php.net/manual/en/book.mysqli.php |
Revision as of 22:05, 2 June 2008
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 <?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; } ?>
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
- by exploiting a SQL Injection vulnerability Authentication you can authenticate as :
- 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 all Database, or interact with DBMS underlying Operating System.
Description
PHP preventing SQL Injection
DBMS authentication credentials
When working with a DBMS through an authenticated connection application developers should be very carefull on how, and subsequently where, store authentication credentials to query such a backend engine. A configuration file with .inc extension should be avoided if left world wide readable by a web server since it's content can be easy retrieved. Such a issue can be easly fixed by:
- Deny remote access to .inc files
<Files ~ “\.inc$”> Order allow,deny Deny from all </Files>
- Secure File Inclusion
<?php if (!defined('SECURITY_INCLUDE_TOKEN') || SECURITY_INCLUDE_TOKEN != 'WfY56#!5150'){ define ('DBMS_CONNECTION_STRING','mysql://owaspuser:owasppassword@localhost:3306'); .... }
<?php define('SECURITY_INCLUDE_TOKEN', 'WfY56#!5150'); include 'dbms_handler.php'; .. ?>
- php_value
/etc/apache2/sites-enabled/000-owasp <VirtualHost *> DocumentRoot /var/www/apache2/ php_value mysql.default_host “127.0.0.1” php_value mysql.default_user “owaspuser” php_value mysql.default_password “owasppassword” .... .... .... </VirtualHost>
dbmshandler.php <?php function iMySQLConnect() { return mysql_connect(); } ?>
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 ''' rather than \'
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:
- MySQL: mysql_real_escape_string
- PostgreSQL: pg_escape_string
function sEscapeString($sDatabase, $sQuery) { $sResult=NULL; switch ($sDatabase) { case "mysql": $sResult = mysql_real_escape_string($sQuery); break; case "postgresql": $sResult = pg_escape_string($sQuery); break; case "mssql": $sResult = str_replace("'", "''",$sQuery); break; case "oracle": $sResult = str_replace("'", "''",$sQuery); break; } return $sResult; } }
Since both Oracle and Microsoft SQL Server connectors doesn't have a real escape_string function software developer can create his own escapeing functions or use addslasshes().
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 */ ... ... }
Prepared Statements
Prepared Statements is the ability to preparse and generate an execution plan for SQL Queries. Such an execution plan will be instantiated with typed parameters. If params are of incorrect type or contains a nested query the execution of plan will fails.
<?php function getBookByID($id) { $aBook = NULL; $link = mysqli_connect(); $stmt = $link->stmt_init(); if ($stmt->prepare("SELECT * FROM books WHERE ID =?")) { $stmt->bind_param("i",$id); $stmt->execute(); /* Retrieves book entry and fill $aBook array */ ... ... /* Free prepared statement allocated resources */ $stmt->close(); } return $aBook; } /* MAIN */ /* Cast GET 'id' variable to integer */ $iID = (int)$_GET['id']; $aBookEntry = getBookByID($iID); /* Display retrieved book entry */ ?>
Data Validation
PHP preventing LDAP Injection
LDAP Authentication Credentials
Data Validation
Detecting Intrusions from WEBAPP
Defeating Automated Tools
References
- Ilia Alshanetsky : "architect's Guide to PHP Security" - http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf
- OWASP : "OWASP Guide Project" - http://www.owasp.org/index.php/OWASP_Guide_Project
- PHP : "MySQL Improved Extension" - http://it2.php.net/manual/en/book.mysqli.php