More Books
PHP 5 Unleashed
PHP 5 Unleashed
Table of Contents
Copyright
Lead Author
Contributing Authors
Acknowledgments
We Want to Hear from You!
Reader Services
Introduction
Organization of the Book
Part I. Working with PHP for General Web Development
Chapter 1. Basic PHP Development
How PHP Scripts Work
Basic PHP Syntax
Basic PHP Data Types
Variable Manipulation
Control Structures
User-Defined Functions
Dynamic Variables and Functions
Multiple File PHP Scripts
References
Strings in PHP
Comparing Strings
Advanced String Comparison
Search and Replacement
Formatting Strings
Strings and Locales
Formatting Date and Time Values
Summary
Chapter 2. Arrays
Basic Arrays
Implementing Arrays
More Array Materials
Chapter 3. Regular Expressions
The Basics of Regular Expressions
Limitations of the Basic Syntax
POSIX Regular Expressions
Perl-Compatible Regular Expressions (PCRE)
PCRE Modifiers
A Few Final Words
Chapter 4. Working with Forms in PHP
HTML Forms 101
Working with Form Submissions in PHP
Summary
Chapter 5. Advanced Form Techniques
Data Manipulation and Conversion
Form Data Integrity
Form Processing
Summary
Chapter 6. Persistent Data Using Sessions and Cookies
HTTP Cookies
PHP Sessions
Advanced Sessions
Summary
Chapter 7. Using Templates
The What and Why of Templates
The Smarty Template Engine
Summary
Part II. Advanced Web Development
Chapter 8. PEAR
What Is PEAR?
Getting and Installing PEAR
Using the PEAR Package Manager
Using the PEAR Website
Using PEAR Packages in Applications
Summary
Reference
Chapter 9. XSLT and Other XML Concerns
Relating XML to HTML
Using XSLT to Describe HTML Output Using XML Input
PHP4 and XSLT Using the DOM XML Module
PHP4 and XSLT Using the XSLT Module
PHP5 and XSLT
Accessing XML Data Using SimpleXML
Generating XML Documents Using PHP
Summary
References
Chapter 10. Debugging and Optimizations
Debugging Your PHP Scripts
Optimizing Your PHP Scripts
Summary
Chapter 11. User Authentication
Authenticating Users in PHP
Securing PHP Code
Summary
Chapter 12. Data Encryption
Shared Secret Versus Public Key
Shared Secret Algorithms
Public Key Cryptography
Using Public Keys in PHP
Summary
Chapter 13. Object-Oriented Programming in PHP
Why Objects?
Creating Basic Classes
Advanced Classes
Special Methods
Class Autoloading
Object Serialization
Exceptions
Iterators
Summary
Chapter 14. Error Handling
The PHP Error-Handling Model
What to Do About Errors
The Default Error Handler
Error Suppression
Custom Error Handlers
Causing Errors
Putting It All Together
Summary
Chapter 15. Working with HTML/XHTML Using Tidy
Introduction
Basic Tidy Usage
Tidy Configuration Options
Using the Tidy Parser
Applications of Tidy
Summary
Chapter 16. Writing Email in PHP
The MIME Protocol
Implementing MIME Email in PHP
Summary
Part III. Building Applications in PHP
Chapter 17. Using PHP for Console Scripting
Core CLI Differences
Working with PHP CLI
CLI Tools and Extensions
Summary
Chapter 18. SOAP and PHP
What Are Web Services?
Installation
Creating Web Services
Consuming Web Services
Looking for Web Services
Summary
Chapter 19. Building WAP-Enabled Websites
What Is WAP?
System Requirements
Introduction to WML
Serving WAP Content
Sample Applications
Summary
Part IV. I/O, System Calls, and PHP
Chapter 20. Working with the File System
Working with Files in PHP
File Permissions
File Access Support Functions
Summary
Chapter 21. Network I/O
DNS/Reverse DNS Lookups
Socket Programming
Network Helper Functions
Summary
Chapter 22. Accessing the Underlying OS from PHP
Introduction
Unix-Specific OS Functionality
Platform-Independent System Functions
A Brief Note About Security
Summary
Part V. Working with Data in PHP
Chapter 23. Introduction to Databases
Using the MySQL Client
Basic MySQL Usage
Summary
Chapter 24. Using MySQL with PHP
Performing Queries from PHP
A MySQLi Session Handler
What Is a Custom Session Handler?
Summary
Chapter 25. Using SQLite with PHP
What Makes SQLite Unique?
Basic SQLite Functionality
Working with PHP UDFs in SQLite
Odds and Ends
Summary
Chapter 26. PHP's dba Functions
Preparations and Settings
Creating a File-Based Database
Writing Data
Reading Data
Sample Application
Conclusion
Part VI. Graphical Output with PHP
Chapter 27. Working with Images
Basic Image Creation Using GD
Using the PHP/GD Drawing Functions
Working with Colors and Brushes
Using Fonts and Printing Strings
General Image Manipulation
Other Graphics Functions
Summary
Chapter 28. Printable Document Generation
A Note Regarding the Examples in This Chapter
Generating Dynamic RTF Documents
Generating Dynamic PDF Documents
Related Resources
Part VII. Appendixes
Appendix A. Installing PHP5 and MySQL
Installing PHP5
Installing MySQL and PHP Modules
Installing PEAR
Appendix B. HTTP Reference
What Is HTTP?
PHP Programming Libraries for HTTP Work
Understanding an HTTP Transaction
HTTP Client Methods
What Comes Back: Server Response Codes
HTTP Headers
Encoding
Identifying Clients and Servers
The "Referer"
Fetching Content from an HTTP Source
Media Types
Cookies: Preserving State and a Tasty Treat
Security and Authorization
Client-Side Caching of HTTP Content
Appendix C. Migrating Applications from PHP4 to PHP5
Configuration
Object-Oriented Programming (OOP)
New Behavior of Functions
Further Reading
Appendix D. Good Programming Techniques and Performance Issues
Common Style Mistakes
Common Security Concerns
Style and SecurityLogging
Summary
Appendix E. Resources and Mailing Lists
Relevant Websites
Mailing Lists and Newsgroups
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z

Securing PHP Code

At the end of this chapter, we will provide you with some common security flaws in PHP code and show you how to avoid them. We will also cover some general security issues that are independent from PHP.

Register_Globals

The php.ini setting register_globals = On is believed to be one of the reasons why PHP has so many fans nowadays. Working with form data, cookies, or sessions was so easyjust use $name, and you had access. Unfortunately, this also made many users create really stupid code. Following is one example, a modified version of the password checks from earlier in the chapter:

if ($name == "php5" && $pass == "cool") {
  $auth = true;
}

if ($auth) {
  $_SESSION["username"] = $user;
  // now, the redirection stuff
  // ...
}

At first glance, this code works well. If the wrong username/password combination is provided, the variable $auth is not set and the session variable is not created. However, what if a malicious (or experimenting) user were to call this script like this: http://servername/login.php?auth=1what would be the effect?

The answer is that because of the GET variable auth, the variable $auth would already exist, the user would be believed as already logged in, and the session variable would be set. A security compromise was achieved by adding seven characters to a URL.

Some might say that the basic reason for the security flaw is that the variable $auth has not been initialized yet. And yes, if the code is changed so that $auth has a default value of false, the exploit does not work any longer:

$auth = false;
if ($name == "php5" && $pass == "cool") {
  $auth = true;
}

if ($auth) {
  $_SESSION["username"] = $user;
  // now, the redirection stuff
  // ...
}

However, there is danger right around the corner. If register_globals is set to on, the check whether a user is already logged in could be changed, as wellfrom

if (!isset($_SESSION["username"])) {

to

if (!isset($username)) {

You might guess how this could be overcome: http://servername/page.php?username=Bill. All you wanted is to access the session variable username, but $username does also grant access to the GET variable username, enabling the exploit.

Therefore, one recommendation for secure PHP code is to turn register_globals off. This has the following advantages:

  • No more cheap exploits by adding data to the URL.

  • You then have to explicitly access the variable using $_GET, $_POST, $_COOKIE, $_SERVER, and so on. If you want to read out a cookie, you get only cookies, no GET or POST data.

  • Using $_GET, $_POST, and the like works independently of the PHP configuration. If you rely, however, on register_globals and your hosting partner decides to turn this feature off, you have to rewrite your code.

It has to be noted that the decision to turn register_globals off by default (introduced in PHP version 4.2.0) was not an easy one; many core developers found that an unnecessary step. The most prominent one is Rasmus Lerdorf, himself, by the way.

Although this configuration change was noted with bold letters in the release note and also mentioned on the php.net home page, there are still articles from 2003 where globals are used. The authors obviously have a rather old PHP installation, with globals still turned on. New users, on the other hand, have globals turned off, and the scripts will not work. So make it better than bad authorsturn register_globals off on all your machines. You may have to type a little bit more, but your script then should work almost everywhere.

TIP

If you do want to use globals, one secret is that the PHP function import_request_variables() converts the superglobals into the variable names you once were used to.


Maximum Error

Let's get back to the example with the globals once more. In the first faulty code, all could have been avoided if there was a warning when uninitialized variables are used. Unfortunately, the standard error reporting value in php.ini is the following:

error_reporting = E_ALL & ~E_NOTICE

That means that all errors are reported, but no notices. In many cases, this is a bad idea. If you access an uninitialized variable, this sometimes happens as intended; however, at other times this could be a typo. Therefore, tune error_reporting to maximum reporting. Nobody likes error messages, but it should be your primary goal to write code that creates zero error messages and warnings. Here is the appropriate setting:

error_reporting = E_ALL

Again, think of providers that set error_reporting at their will. You might work with E_ALL & ~E_NOTICE at home, but your hoster could use E_ALL, which would result in ugly notices within your code. To be compatible with all settings of error_reporting, set your system to E_ALL.

TIP

If you do not want to use maximum error reporting (or if you have inherited a lot of code and cannot change it over the weekend), the PHP function error_reporting() lets you set error reporting on a per-page basis.


New in PHP 5 is the error level E_STRICT (value: 2048). This is even stricter than E_ALL and includes additional warnings when deprecated PHP functions are used.

When you are finished with an application and want to go live with it, you should disable error reporting completely. But that does not mean that you should change the configuration value for error_reporting; instead, you should tell PHP not to send any errors to the client:

display_errors = Off

However, you do want these errors to appear in your Web server's error log; therefore, set log_errors to On.

Trust No OneEspecially Not User Data

Whenever you get data from your users, prepare for the worst. In a perfect world, all users enter perfect data (in perfect forms). However, you cannot assume that this will happen. Conclusion: check all user data thoroughly. If a user enters his or her age, you should check itis it numerical at all?

if (!is_numeric($_POST["my_age"])) {
  // error handling goes here
}

Are you prompting the user to provide the email address, and then you write it into a database field? If the database field accepts 50 characters, but the email address is longer than that, something bad might happen. Either the information gets truncated, or even worse, you get a database error message. Therefore, you should first trim() the data and then check its length.

NOTE

For a more sophisticated checking of user input, regular expressions are an excellent tool.


Printing User Data

One specialized case for potentially malicious user data is when you output this data. As a general rule, always check your output! Imagine a guest book where users can leave messages. If you output the text without previously checking it, this might lead to some undesirable results, especially if HTML formatting is used. A <table> element that is not closed leads to a blank page on Netscape 4; imagine what JavaScript code could do to the page layout. Either use strip_tags() to remove all HTML markup, or even better, convert the user data to printable text using htmlspecialchars().

Working with Files

If at any point in your Web application you are working with files, there is a possible danger. A lot of CMS (content management systems) work with URLs like this:

http://servername/renderer.php?template=whatever.xml

So far, so good, but what happens if a nonexisting filename is provided?

http://servername/renderer.php?template=does-not-exist.xml

A PHP error message such as could not open stream should be avoided. Catch the error and provide a custom error message or maybe even an automated email to the Webmastereither it's a dead link or it might be a cracking attempt, but both scenarios are worth noticing.

However, there is one more thing to note. Imagine the same script is called like this:

http://servername/renderer.php?template=/etc/passwd

or like this:

http://servername/renderer.php?template=../../../../etc/passwdp

If you just read in a template, replace some placeholders, and then print everything to STDOUT, some sensitive files might be at risk. Therefore, do not only check whether an existing file is to be opened, also check whether the file may be opened.

Note, too, that each file operation is a system call. Maybe someone tries to give you a shell command as a filename. Then this command would be executed, if you do no thorough checking. If in doubt, apply the PHP function escapeshellarg(), which puts single quotes around the parameter and escapes special characters.

Working with Databases

Extremely nasty security flaws occur when databases come into play. On many pages, something like this appears:

db_query("SELECT * FROM table WHERE id=" . $_GET["id"]);

Nice try, but what if the ID parameter has the value "0; DELETE FROM table"? Therefore, use at least quotes:

db_query("SELECT * FROM table WHERE id='" . $_GET["id"]) . "'";

But this code could be broken, as well; ID must just have this value: "'; DELETE FROM table; SELECT * FROM table WHERE id='".

It is fairly easy to guess how the parameter has to look to at least break the code. Just using an apostrophe generates error messages on far too many pages. Therefore, check user data; check SQL statements.

Some love it, some hate itPHP's magic quotes. To all special characters in user data, backslashes are added; "McDonald's" becomes "McDonald\'s", and so on. If magic quotes are turned on, you do not have to worry about adding slashes by yourself; if not, use addslashes(), which does the same task for you.

This is rather MySQL specific; some other database systems, however, offer two differences:

  • Single quotes within a SQL string must not be escaped using the backslash, but by doubling them: 'McDonald's' is wrong; 'McDonald''s' is correct.

  • Other special characters must be disabled, such as square brackets.

For this case, use one or more regular expressions to escape these characters:

$str = preg_replace("'", "''", $str);

TIP

An efficient way to do multiple replacing is using strtr().


Also, check for database errors and catch them. Getting an error message that reads connection to database failed is bad enough; a more detailed error message, maybe including the name of the database server and its port, is even worse, because that gives an attacker additional information about your installation.