Published on the O'Reilly Network
(http://www.oreillynet.com/)
http://www.onlamp.com/pub/a/php/2003/03/20/php_security.html
See this
if you're having trouble printing code examples
Ten Security Checks for PHP, Part 1
by Clancy Malcolm
03/20/2003
Web applications have become a popular way to provide global access to
data, services, and products. While this global access is one of the
Web's underlying advantages, any security holes in these applications are
also globally exposed and frequently exploited. It is extremely easy to
write applications that contain unintentional security holes. This is
demonstrated by the range of common web applications, including
PHPMyAdmin, PHPShop and FreeTrade, that have contained major security
holes. The source code is often required to identify these holes, but it
is common to make the source code of these applications available to the
public. This article provides five steps to help identify or avoid such
security holes in applications written using PHP.
Avoid Using Variables When Accessing Files
Consider the following code:
// $lib_dir is an optional configuration variable
include($lib_dir . "functions.inc");
or worse still:
// $page is a variable from the URL
include($page);
The user could set the $lib_dir or $page
variables and include files such as /etc/passwd or remote
files such as http://www.some-site.com/whatever.php with
malicious code. This malicious code could potentially delete files,
corrupt databases, or change the values of variables used to track
authentication status.
What to Look For
Search code for the following functions:
readfile
fopen
file
include
require
Possible Fixes or Improvements
Avoid using variables as file names. The $lib_dir
variable above could be replaced with a value defined by the PHP
define function.
Check the file name against a list of valid file names. For
example,
$valid_pages = array(
"apage.php" => "",
"another.php" => "",
"more.php" => "");
if (!isset($valid_pages[$page])) {
// Abort the script
// You should probably write a log message here too
die("Invalid request");
}
If you must really use a variable from the browser, check the
variable's value using code like the following:
if (!(eregi("^[a-z_./]*$", $page) && !eregi("\\.\\.", $page))) {
// Abort the script
// You should probably write a log message here too
die("Invalid request");
}
See Do Not Trust Global Variables for further
steps on ensuring variables cannot be maliciously set.
Use the allow_url_fopen and open_basedir
configuration variables to limit the locations where files can be opened
from.
Escape characters in SQL statements
A common mistake is to use a variable value supplied by the user or the
URL in an SQL query without escaping special characters. Consider the
following fragment of code from a script designed to authenticate a
username and password entered in a HTML form:
$query = "SELECT * FROM users WHERE username='" . $username . "'
AND password='" . $password . "'";
// the record exists function is defined elsewhere
if (record_exists($query)) {
echo "Access granted";
} else {
echo "Access denied";
}
his code would work when accessed using
check.php?username=admin&password=x . However, if the code
were accessed using
check.php?username=admin&password=a%27+OR+1%3Di%271 (and
if magic_quotes_gpc were disabled) then the password
condition becomes Password='a' or 1='1' so that the admin
user record would always be returned regardless of the password it
contained.
This problem is partly avoided when the
magic_quotes_gpc variable is on in the php.ini
file, meaning that PHP will escape quotes in GET, POST, and cookie data
using the \ character. However,
magic_quotes_gpc is frequently disabled because it could make
other code behave strangely. Given a line containing echo
$username in the above code fragment, any occurrences of
' would be replaced by \' ). Furthermore the
magic_quotes_gpc variable does not protect against variable
values obtained from sources such as database records or files which a
malicious user may have already modified during normal program
operation.
What to Look For
Search for the query functions for your database. For example, if you
are using MySQL, search for usage of the mysql_db_query
function.
Possible Fixes or Improvements
Use the built-in addslashes function or a similar
function to escape quotes and backslashes in SQL statements with
backslashes.
Enabling magic_quotes_gpc may help, but don't rely
upon it. (Enabling this setting and using addslashes will
produce errors).
If you are using variables which you expect to contain numbers in
your SQL statement, ensure that they really do contain numbers. You can
use various built-in PHP functions including sprintf ,
ereg and is_long , to perform this
check.
If the register_globals option is enabled, PHP will create
global variables for each GET, POST, and cookie variable included in the
HTTP request. This means that a malicious user may be able to set
variables unexpectedly. Consider the following code aimed to allow
anonymous access to a single article and require authentication for all
other articles:
// Assume $article_id is set by the URL
if ($article_id == 0) {
$guest_ok = true;
}
if (!$guest_ok) {
// Check user is authenticated using a function defined elsewhere
check_auth();
}
This code may appear to work, because the $guest_ok
variable will generally be initialized to false. However, if a malicious
user includes guest_ok=1 in the URL, he will be granted
access to any article in the system.
A similar problem can arise when you perform security checks when
showing links to pages but do not perform security checks on the linked
pages themselves. In a system where users are granted access to a select
list of articles, you should perform security checks when producing the
list of available articles and when displaying an article
selected from the list. Without this checking, a malicious user could
type URLs for articles to which he should not have access and view the
article successfully. Another common variation of this problem is to
implement a "Remember My Login" feature by storing a user identifier in a
cookie, allowing users to change their cookie value to login as whomever
they want.
What to Look For
This problem can appear almost anywhere in your code. Pay careful
attention to the following areas:
- Authentication and permission checking code
- Use of variables before they are initialized. (You can set the
error_reporting configuration variable to give a warning
whenever uninitialized variables are used.)
- Use of variables designed to be set by GET or POST requests.
Possible Fixes or Improvements
Disable register_globals in your php.ini
file. After making this change, you will need to use the
$HTTP_GET_VARS and $HTTP_POST_VARS associative
arrays to access GET and POST inputs instead of using global variables.
This can be tedious, but also far more secure.
If a "Remember My Login" function is required, include a password
or a hard to guess random identifier in the cookie. (A "Remember My
Login" function can still produce other holes such as malicious user who
shares a machine with a legitimate user to gain access.)
Write code to initialize all global variables. The previous code
fragment could be improved by initializing $guest_ok to false
at the start of the script.
Ensure session variables really do come from the session and not from a
malicious user.
Write code to check that a global variable is not in the
$HTTP_POST or $HTTP_GET associative
arrays.
Avoid False Uploads
File uploads can suffer from a severe case of the untrusted global
variables problem that is worth considering as an additional problem.
When a file is uploaded, a PHP script is given a variable that provides
the name of the temporary file where PHP saves the uploaded file.
However, the user could construct a URL that sets this variable to a
malicious value such as /etc/passwd and not upload a file.
The responding script may then copy that file to an accessible location or
display the file's contents to the user.
What to Look For
Examine all scripts that respond to file uploads. Searching for
type="file" may help identify these scripts.
Possible Fixes or Improvements
Recent versions of PHP have the is_uploaded_file and
move_uploaded_file functions that allows the programmer to
ensure that they are working with uploaded files.
If you are not sure that your code will be running on a recent
version of PHP, set the upload_tmp_dir configuration setting
and then perform input checking to ensure that the file you are working
with is in this directory.
Escape HTML Characters in Text
What happens if somebody puts a <blink> tag in a
posting to a discussion board? If you don't escape HTML characters in
text either before you save or display it, all subsequent text on the page
could be blinking. More severe versions of this attack are also possible;
for example an attacker could write JavaScript that takes the browser to a
competitor's site.
What to Look For
Identify pages which display text entered by untrusted users.
Possible Fixes or Improvements
Escape HTML appropriately either before you save it or before you
display it. You can use PHP's built-in functions
htmlspecialchars or htmlentities for this
purpose.
If you want untrusted users to use HTML for formatting, you should perform
validation to restrict the available HTML tags to a basic tags set, like
<b> and <i> .
Further Ideas
Part two of this article will feature five more security checks for
PHP. In the meantime, here are three ideas to keep in mind as you design
your application.
Encrypt or use hashes of passwords when storing them (PHP's
md5 function is useful for this)
Don't store credit card numbers: it is generally better to use a
third-party payment gateway instead
Enforce strong passwords. Password strength requirements vary from
application to application, but consider, at a minimum, enforcing
passwords that are at least six characters long and contain some
non-alphanumeric characters.
Clancy Malcolm
is a private web application consultant and contributes to numerous
open source projects.
Return to Related Articles from the O'Reilly Network .
oreillynet.com Copyright © 2003 O'Reilly & Associates, Inc. |