PHP DevCenter

oreilly.comSafari Books Online.Conferences.

We've expanded our LAMP news coverage and improved our search! Search for all things LAMP across O'Reilly!

Search
Search Tips

advertisement

Listen Print Discuss Subscribe to PHP Subscribe to Newsletters

PHP Foundations PHP Security, Part 1

by John Coggeshall
07/31/2003

In my last two columns (Common Style Mistakes, part one and Common Style Mistakes, part two), I discussed some common bad practices to avoid when writing PHP scripts which can make them more difficult to read and more prone to bugs. In today's column I'll change gears and discuss the meat of this series: the importance of security when working with PHP.

The Importance of Thinking About Security

More than meets the eye

The most effective and often overlooked measure to prevent malicious users from compromising your scripts is to consider the possibility it could happen when you write them. It's s important to be mindful of the possible security implications of your code.

Consider the following example function designed to simplify the life of a developer who writes a great many text files from PHP scripts:

<?php
function write_text($filename, $text="") {
	static $open_files = array();

	// If filename is null, close all open files
	if ($filename == NULL) {
		foreach($open_files as $fr) {
			fclose($fr);
		}
		return true;
	}
	$index = md5($filename);    
       
	if(!isset($open_files[$index])) {
		$open_files[$index] = fopen($filename, "a+");
		if(!$open_files[$index]) return false;
	}
	fputs($open_files[$index], $text);
	return true;
}?>

This function takes two parameters by default, the filename and the text to write to that file. The function will first check to see if it has already opened that file in the past; if it has, it will reuse the old file reference. Otherwise, it will create one if one doesn't exist. In either case, the text is then written to the file. If the filename passed to the function is NULL, then all the opened file references are closed. An example usage is provided below.

If the developer is writing a number of text files in this manner, this function will make his code look much cleaner and easier to understand. Let's assume that this function lives in a separate file which is included in the scripts which require the function. Here's one of the scripts where it's used, called quotes.php:

<html><body>
<form action="<?=$_SERVER['PHP_SELF']?>" method="get">
Choose the nature of the quote:
<select name="quote" size="3">
<option value="funny">Humorous quotes</option>
<option value="political">Political quotes</option>
<option value="love">Romantic Quotes</option>
</select><br />
The quote: <input type="text" name="quote_text" size="30" />
<input type="submit" value="Save Quote" />
</form>
</body></html>

<?php
    include_once('write_text.php');

    $filename  = "/home/web/quotes/{$_GET['quote']}";
    $quote_msg = $_GET['quote_text'];

    if (write_text($filename, $quote_msg)) {
       echo "<center><hr><h2>Quote saved!</h2></center>";
    } else {
       echo "<center><hr><h2>Error writing quote</h2></center>";
    }
    write_text(NULL);
?>

As you can see, this developer has used the write_text() function created previously to develop a system to allow users to submit their favorite quotes, which are then saved to a text file. Unfortunately, though the developer may not know it, this script could also allow a malicious user to compromise the security of the web server.

Perhaps right now you are scratching your head and wondering exactly how such an innocent looking script poses such a security risk. Instead of asking you to figure it out yourself, consider the following URL, remembering that the script itself is called quotes.php:

http://www.somewhere.com/fun/quotes.php?quote=different_file.dat&quote_text=garbage+data

What will happen when this URL is presented to the web server? Obviously the quotes.php script will be executed; but instead of writing a quote to one of the three desired files, a completely new file called different_file.dat will be written with a string garbage data inside of it. Obviously, this is not desired behavior at all. In fact, a malicious user might even be able to create an account by accessing the Unix password file by specifying ../../../etc/passwd for the quote parameter (although that would require the web server to be running scripts as a superuser, and if that is the case you should stop reading this and fix that right now). Perhaps the most serious implication of this script is it could even be used to allow someone to write and execute arbitrary PHP scripts, if the /home/web/quotes/ directory were accessible from a browser. The evil possibilities are endless.

There are several solutions. If you only need to write a few files in the directory, consider using an associative array to store the file names. If the user input exists in the associative array, it's safe to write. Another option is to strip out all non-alpha and non-numeric characters, to make sure there are no directory separators. Yet another idea is to check the file extension to make sure it won't be executed by the web server.

Related Reading

Programming PHP
By Rasmus Lerdorf, Kevin Tatroe

The bottom line is simple. As a developer you must be aware of more than what your scripts do under the desired circumstances. What will happen if invalid data is entered into a form element? Is there any way a malicious user could make your script behave in an unintended way? What measures are being taken to prevent these attacks? Your web server and PHP scripts are only as safe as the weakest security link, so it's important to identify these possible weak links before they're identified for you.

Common security-related mistakes

To give you a few pointers, here's a brief and incomplete list of coding or administrative failures which can compromise security:

  • Mistake 1. Trusting data

    As will be the theme throughout my discussion of security as it relates to PHP scripts, you should never trust data provided by an outside source. No matter if it comes from a user-submitted form, a file in the filesystem, or an environment variable, nothing should simply be taken at face value. All user input should be validated and formatted to make sure it's safe.

  • Mistake 2. Storing sensitive data in the web tree

    Any and all sensitive data should always be stored in a separate file from the script that needs it and stored in a directory that cannot be accessed via a web server request. When the sensitive data in question is needed, that data can be included in the appropriate PHP script via an include or require statement.

  • Mistake 3. Not implementing recommended security precautions

    The PHP manual contains an entire section devoted to security precautions when using and coding PHP scripts. The manual (almost) always clearly notes on a case-by-case basis when a potential security risk exists and how that risk can be minimized. Again, malicious users rely on developers and system administrators failing to pay attention to security concerns in order to gain access to their systems. Heeding these warnings and acting appropriately significantly diminishes the chance of a malicious user being able to do any real damage to your system.

More on security soon

I cannot stress enough the importance of thinking about security in order to protect your servers from malicious users. Now you should be looking at your scripts in a whole new light. With a little experience, soon you'll be catching these potential security lapses before you even write the code to create them. The next column will discuss a few more common ways security is compromised in PHP scripts and the steps you as a developer can do to minimize them.

John Coggeshall is a a PHP consultant and author who started losing sleep over PHP around five years ago.


Read more PHP Foundations columns.

Return to the PHP DevCenter.



Have a question about John's three rules? Ask him here.
You must be logged in to the O'Reilly Network to post a talkback.
Post Comment
Full Threads Oldest First

Showing messages 1 through 6 of 6.

  • open_basedir limits
    2005-06-08 14:45:48  Bread [Reply | View]

    The php ini directive of 'open_basedir' may be fine when related to php filesystem functions, but the danger is not confined to just that.

    The usage of system based functions is a great risk too. Imagine if a mock shared server is running apache as nobody and someone used system() to 'cat' a configuration file for a forum or such. The dangers are immense.. instead of having an account with 'writable' files in, this malicious user now has access to a database full of account hashes and email addresses.

    The best path of action, is to have php running as the user id(As someone mentioned above). One possible method to achieve this is by running php as a CGI and through suEXEC.
  • Just some Comments on this column
    2004-12-31 23:47:25  phpORcaffine [Reply | View]

    I have to say, I am not a fan of $_GET however, it does have a place. In the above example, POSTING (method="POST") would be more secure than the GET method. Anytime you put data in a Global NameSpace area(address bar) you are asking for trouble.

    If a get method cannot be avoided I recommend using a hash/encrypt function on the data before it is placed into the address bar. Also use "key characters" in front of the string so that you can detect the characters once you GET the data, if the "key characters" do not exist on the string, then NULL the value of the string because the strings data obviously didn't come from your script and is probably an attack attempt.

    Another secure method would be to store all of the valuse in a "transfer" database. Just set up a database with tables that apply to your site and use it to transfer the variable data, so the user never sees it.
  • Security or Reliability/Robustness
    2003-10-14 05:07:27  anonymous2 [Reply | View]

    A general comment (I'm not a PHP coder but interested in PHP, but I'm knowledgable in security/trust).

    I'm wondering if you deal with PHP security or if you're confusing it with reliability of PHP code.

    More precisely: security generally encompasses aspects like authentication/identification, authorisation/acces control, confidentiality/privacy, and some other properties.

    But the questions you want to answer are "What will happen if invalid data is entered into a form element? Is there any way a malicious user could make your script behave in an unintended way? What measures are being taken to prevent these attacks?"

    "Invalid" and "unintended" clearly point to the reliability property, while "failures" would be more appropriate than "failures".
  • no PHP security on shared web servers
    2003-08-08 08:17:52  idallen [Reply | View]

    When you do your article on security, please address a common issue that
    seems to be a dirty trade secret: the lack of security for PHP-accessed
    files on a shared web server.

    A huge number of people have purchased web hosting accounts on shared
    servers - servers where your account is only one of many. Advice against
    "storing sensitive data in the web tree" is of limited benefit if you
    share a server with other accounts; the advice only protects the files
    from being accessed via the web server program itself. It doesn't protect
    the files from access by other means, such as PHP scripts written by
    other people sharing the same server.

    Unlike CGI scripts, all PHP code runs as the userid of the web server,
    no matter in whose account it resides. That means that if *you* write
    a PHP script that can access a file on your server, *anyone else* on
    the same server as you can write a PHP script to access the same file,
    in the same way.

    You have no protection from someone on the same server as you writing
    a PHP script to read and write the same files that you read and write.

    It doesn't matter where you put the files, if your PHP code can see them,
    so can PHP code (or CGI code, or even shell scripts) written by other
    people on the same server.

    Only shared servers that implement a true virtual account environment are
    resistant to this type of file system browsing (e.g. Ensim). Common web
    hosting environments such as cPanel have no protection.

    Note that turning on safe_mode on the PHP server doesn't solve this
    problem. If you have a PHP script that can read/write a file; anyone
    else on your server can write a PHP script to read/write the same file.

    There are ways to have PHP scripts execute using the userid of the
    file containing the script. This makes PHP scripts as secure as CGI
    scripts, and it can solve the problem; however, it is places a large
    load on the web server and few web hosting providers do this.

    An alarming number of web hosting providers fail to tell their clients
    that the wonderful PHP/database/bulletin-board systems they offer
    are completely open to compromise by other users of the same server.
    (I actually posted information about the possibility of compromise on
    the public forum pages at one web provider and they took it off and told
    me not to do that again. They don't want their clients to know.)
    • no PHP security on shared web servers
      2003-08-22 06:57:48  anonymous2 [Reply | View]

      The Web hosting company could add an open_basedir directive in the config file for each client's "root" (top-level or virtual host) folder:

      Client ABC gets the directive:
      open_basedir = '<somepath>/clientABC/'

      Client XYZ gets the directive:
      open_basedir = '<somepath>/clientXYZ/'

      The default directive for PHP is:
      open_basedir = '.'
      (If not explicitly set for a folder, PHP scripts can only fopen/include from the current folder)

      This seems to make it substantially more difficult for PHP code in /clientABC/ to get to code in /clientXYZ/ - although I won't say it is impossible...
  • script fails to empty array of MD5 sums
    2003-08-08 07:44:48  idallen [Reply | View]

    After closing all the open files, remember to empty
    the array of remembered open files...


Recommended for You

  1. Cover of Learning PHP & MySQL
    Learning PHP & MySQL
    Print: $29.99
    Ebook: $23.99
  2. Cover of Upgrading to PHP 5
    Upgrading to PHP 5
    Print: $29.95
  3. Cover of Learning PHP and MySQL
    Learning PHP and MySQL
    Print: $29.99
    Ebook: $20.99
  4. Cover of PHP Pocket Reference
    PHP Pocket Reference
    Print: $9.95

Tagged Articles

Post to del.icio.us

This article has been tagged:

php

Articles that share the tag php:

Understanding MVC in PHP (477 tags)

The PHP Scalability Myth (123 tags)

The Dynamic Duo of PEAR::DB and Smarty (53 tags)

PHP Form Handling (43 tags)

Very Dynamic Web Interfaces (39 tags)

View All

security

Articles that share the tag security:

Secure RSS Syndication (169 tags)

Google Your Site For Security Vulnerabilities (74 tags)

Building a Desktop Firewall (64 tags)

The Next 50 Years of Computer Security: An Interview with Alan Cox (42 tags)

Protect Yourself from WiFi Snoops (40 tags)

View All

programming

Articles that share the tag programming:

Rolling with Ruby on Rails (1374 tags)

Very Dynamic Web Interfaces (279 tags)

Ajax on Rails (231 tags)

Understanding MVC in PHP (202 tags)

A Simpler Ajax Path (186 tags)

View All

onlamp

Articles that share the tag onlamp:

Live Backups of MySQL Using Replication (4 tags)

Rolling with Ruby on Rails, Part 2 (3 tags)

Building a FreeBSD Build System (3 tags)

Design by Wiki (2 tags)

Enhanced Interactive Python with IPython (2 tags)

View All

Sponsored Resources

  • Inside Lightroom
Advertisement

Sponsored by:

O'Reilly Media

©2010, O'Reilly Media, Inc.
(707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
About O'Reilly
Academic Solutions
Authors
Contacts
Customer Service
Jobs
Newsletters
O'Reilly Labs
Press Room
Privacy Policy
RSS Feeds
Terms of Service
User Groups
Writing for O'Reilly
Content Archive
Business Technology
Computer Technology
Google
Microsoft
Mobile
Network
Operating System
Digital Photography
Programming
Software
Web
Web Design
More O'Reilly Sites
O'Reilly Radar
Ignite
Tools of Change for Publishing
Digital Media
Inside iPhone
makezine.com
craftzine.com
hackszine.com
perl.com
xml.com

Partner Sites
InsideRIA
java.net
O'Reilly Insights on Forbes.com