PHP DevCenter
oreilly.comSafari Books Online.Conferences.


PHP Foundations PHP Security, Part 3

by John Coggeshall

Welcome to another installment of PHP Foundations. Last time, I discussed the potential security breaches that can occur when using system calls from PHP scripts (and some methods of protecting yourself from them). As the last part of my series focusing on the pitfalls and techniques involved when writing secure PHP applications, this article will not introduce any new potential security breaches. Rather, today I will finish my discussion of security by introducing the tools PHP provides to handle error logging and reporting and summarize the main points that I have covered in this series.

Logging and Security

In order for malicious users to take advantage of your programs, they must first know of your program's weaknesses. To do so, a malicious user needs to probe your web applications to gather as much information as possible about them. A very effective and common approach is to attempt to cause errors. For example, let's assume a malicious user has entered invalid data into a login form, producing the following standard PHP error message:

Notice: Undefined index: content in /usr/local/apache/htdocs/index.php
on line 22

Related Reading

Secure Coding: Principles and Practices
By Mark G. Graff, Kenneth R. van Wyk

What can be determined from this message? Obviously, the error indicates that there is an array with a key of content that is undefined in the file index.php, on line 22. From this one error message, a malicious user now has a lead on a potential weak point from which to pollute your application's data. Maybe that undefined index is something in $_GET or $_POST. In fact, you could even get a general idea of the code flow of your scripts by paying attention to what line number an error occurs on, under different circumstances. Furthermore, the nature of the error can also provide more detailed information, including where the script stores files on the server (if working with filesystem commands), the format of the queries used by the script (if working with a database), and much more. Although it is rarely desirable for your users to see detailed error messages when something goes wrong, it can even become a security hazard under certain circumstances.

The first step to solving this problem is to take the necessary steps to minimize the potential for runtime errors within your scripts. Any good PHP application should ensure that all variables are defined, or at least checked with isset(), if dealing with superglobal data. However, no matter how much thought in put into your application, it is unrealistic to expect that you've accounted for every possible circumstance. For this reason, security-sensitive web applications also implement error-handling and -logging systems.

The PHP Error-Logging Mechanism

The point of error handling and logging, at least when it comes to security, is to deny a malicious user information about your system, while still providing the developer access to that information. An appropriate error-logging system will record attempts to compromise the security of your application, giving you the information needed to strengthen your application's security where and as necessary.

Implementing a logging system in PHP can be as simple or as complex as you like. PHP internally offers several options to the developer as to how errors are dealt with and logged. For instance, although PHP, by default, will display errors that occur during the processing of a script to the browser, it also can be configured to log those errors without displaying them. This behavior is controlled by the log_errors and display_errors configuration directives in the php.ini file. Turn error display on and off as your development needs change. A common practice is to display errors without logging them during the development and debugging of an application. The finished product will do the opposite: logging, but not displaying, errors.

Although you are familiar with how PHP displays error messages to the browser, where does PHP log errors when logging is enabled? Another configuration directive, error_log, controls the behavior of PHP's error-logging mechanism. This directive can be set to a filename, the string syslog, or completely omitted (the default). When the error_log directive is completely omitted from php.ini, PHP will use the logging facilities provided by the web server (such as the Apache error log) for its logging purposes. If set to a filename, PHP will write all messages to that file, as long as system permissions allow it. If set to the keyword syslog, PHP will log messages via the operating system's logging facilities. On UNIX-based systems, this is the standard OS syslog, and on Windows NT and XP systems, this is the event log on.

Although PHP will automatically take care of logging error messages for you when you are using the internal error handler, when using a custom error handler (discussed later in this article), you must log these errors yourself. To do this, PHP provides the error_log() function, with the following syntax:

error_log($message [, $message_type [, $dest [, $extra_info]]]);

Depending on the value of the optional $message_type parameter (the default is zero), one of the following things will occur:

  • If $message_type is zero, the error message $message will be recorded in the logging facility specified by the error_log configuration directive.
  • If $message-type is 1, the error message $message will be emailed to the address specified by the $dest parameter. Any desired additional mail headers can be specified in the $extra_info parameter.
  • If $message_type is 3, the error message $message will be written to the file specified by the parameter $dest.

Note: You might have noticed that there is no behavior if $message_type is 2. This is a relic from PHP version 3, where remote debugging was provided as part of the standard release. It is no longer available in PHP4.

The error_log() function can be used anywhere for logging, but is used most often as part of a custom error handler. See the PHP manual for examples.

The PHP Error Model

Understanding PHP's error model is almost as important as having an appropriate error-logging mechanism. This model governs what kinds of errors PHP logs, and when and how it logs them. To understand the error model, you must be familiar with the types and meanings of errors that can occur in PHP. This information can be found in the PHP manual as well as the following list:

  • E_ERROR signifies that a serious problem has occurred internally within PHP or a PHP extension (for instance, failure to allocate memory).
  • E_WARNING usually occurs to bring attention to problems that may exist with your code that will cause it to not work properly, such as passing a scalar value to an internal function that expects a complex value such as an array.
  • E_NOTICE is the least significant of the internal errors caused by PHP. This error almost never signifies that the application is performing improperly. Instead, this error message is generally reserved to warn the developer of things such as variables used before initialization.
  • E_CORE_ERROR is identical to E_ERROR in severity, but is only generated during the initial initialization of the engine.
  • E_CORE_WARNING is the non-fatal counterpart of E_CORE_ERROR and is similar to E_WARNING. It is only generated during initial initialization of the engine.
  • E_COMPILE_ERROR signifies a serious error during compilation by the Zend Scripting Engine.
  • E_COMPILE_WARNING is a non-fatal warning, like E_WARNING, generated by the Zend Scripting Engine during compilation.
  • E_USER_ERROR is reserved strictly for use with the trigger_error() function (discussed later). By default, PHP treats this error the same as E_ERROR, displaying the error and halting execution.
  • E_USER_WARNING is reserved strictly for use with the trigger_error() function. By default, PHP treats this error the same as E_WARNING.
  • E_USER_NOTICE is reserved strictly for use with the trigger_error() function. Unlike E_NOTICE errors, which are ignored by default, PHP will display E_USER_NOTICE errors to the user.

Setting the Error Reporting Level

The error_reporting configuration directive determines which error messages are actually logged or displayed to the browser when they occur. This directive is a "bit field," meaning that error messages can be combined together in any way desired using the AND, OR, and NOT Boolean logic operators. In the php.ini file, the ampersand (&) symbol indicates AND, the pipe (|) symbol indicates OR, and the tilde symbol (~) indicates NOT. Therefore, to display or log only serious errors (no warnings or notices), you could use:


Along with the standard error types, the error_reporting directive also allows a special error type, E_ALL, which represents all possible errors, equivalent to ORing each of the error types together. Likewise, E_ALL includes all of the error types. The code below will display or log all errors except those in the E_USER family:

error_reporting = E_ALL & ~E_USER_ERROR & ~E_USER_WARNING & ~E_USER_NOTICE

By default, the error_reporting configuration directive will log or display all errors except E_NOTICE errors.

Regardless of how PHP is configured to handle errors, there are several ways to change what actually happens at runtime. The easiest method is to use the special error-silencing operator @. Place this operator in front of a statement, and PHP will silently ignore any error that occurs during the evaluation of the expression. For instance:

echo @$myvar;

will no longer cause an E_NOTICE error, even though $myvar is undefined. Instead, PHP will silently ignore the error and print nothing.

Another method of altering PHP's configured error handling is by using the error_reporting() function. This function directly modifies the internal value of the error_reporting configuration directive. It has the following syntax:


where the optional $error_value parameter is the new error value. PHP defines constants representing each of the types of errors shown above, which can be combined using PHP's logic operators. The following statement instructs PHP to respond to only serious errors, as per an earlier example:


Regardless of the parameter provided to the error_reporting() function, the return value will always be an integer representing the previously set error_reporting directive value. This allows you to set a custom error reporting level for a small segment of your web application and restore it back to its previous value easily:

$old_error = error_reporting(0); /* Turn off all error reporting */
/* do things here
error_reporting($old_error); /* restore previous error reporting status. */

Custom Error Handlers

Beyond all of PHP's standard error-handling facilities, the language also allows you to define a custom error handler. A custom error handler allows you to strictly control how your web application will respond to an error generated either by PHP internally or triggered by the trigger_error() function. To use custom error handling, define a function of the following form:

function my_handler($error_code, $error_msg [, $error_file [,
$error_line [, $vars]]]) {
    /* Error handling routines here */

As described in the above snippet, any custom error handler must accept $error_code and $error_msg parameters corresponding to the error code (such as E_ERROR) and the error message associated with the error. Your error handler can also optionally accept up to three additional parameters: $error_file, $error_line, and $vars. The first two parameters represent the PHP file and line number where the error occurred. The last parameter, $vars, is an associative array containing the name and value of each variable that was available at the time the error occurred.

Once you have created a function of the correct form, it must be registered with PHP as the active error handler. Use the set_error_handler() function, which has the following syntax:


where $func_name is a string containing the name of the function you defined (using my example, my_handler). When this function executes, it returns the value of the old error handler, which can be saved to restore the previous error handler as desired. When using a custom error handler, there are several things to consider:

  • The custom error handler cannot process "fatal" errors. The error handler will only be called for E_WARNING, E_NOTICE, and the E_USER family of errors. All other errors will be handled by the internal error handler as if there were no custom error handler.
  • When using a custom error handler, PHP will call it when any of the above errors occur, regardless of the setting of the error_reporting configuration directive. It is up to the error handler to use error_reporting() and act accordingly.
  • Unless the custom error handler terminates the execution of the script using die() or exit() statements, PHP will continue executing the script, regardless of the error. It is up to the error handler to stop execution if necessary.

Today's final function is trigger_error(). This function is used to trigger user-defined errors. It has the following syntax:

trigger_error($msg [, $error_code])

where $msg is a string containing an error message describing the error, and the optional parameter $error_code is an error from the E_USER family. PHP will automatically use E_USER_NOTICE when no error code is provided. This function is designed to be used in conjunction with a custom error handler, though if no custom error handler exists, PHP will respond to the error using the internal error handler.

A Final Word on Security

Before I finish this series on security in PHP, I want to wrap up this discussion and recap the major themes that I have discussed over the past several columns. When writing a web application in PHP (or any application in any language), the single biggest thing that you can do to improve the security of your application is to keep potential security implications in mind. Are you using system calls? What are you doing to protect them from being taken advantage of? How will your application respond to invalid user input? What precautions are you taking to filter user input? You should ask yourself all of these questions as you develop.

In the end, any text (including this one) can only teach you so much. Once you have learned the basic concepts, such as logging and data validation, it is up to you to apply them to your application. Diligence and careful attention to detail are the best tools any developer has to ensure the security of his applications. Although malicious users use standard tactics to cause your programs to behave in an unintended way, by the very nature of maliciousness, they will always attempt to do things that you may not have considered.

In my next article, I will switch gears to step back from security. I'll discuss tools to assist you in manipulating and working with data. Until then, happy scripting!

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.

Valuable Online Certification Training

Online Certification for Your Career
Earn a Certificate for Professional Development from the University of Illinois Office of Continuing Education upon completion of each online certificate program.

PHP/SQL Programming Certificate — The PHP/SQL Programming Certificate series is comprised of four courses covering beginning to advanced PHP programming, beginning to advanced database programming using the SQL language, database theory, and integrated Web 2.0 programming using PHP and SQL on the Unix/Linux mySQL platform.

Enroll today!

Sponsored by: