In addition to Safe Mode, PHP provides a number of functions that allow you to place restrictions on the features available to PHP.
You can use the expose_php directive in php.ini to prevent the presence of PHP being reported by the web server, as follows:
expose_php = On
By using this setting, you can discourage automated scripts from trying to attack your web server. Usually, the HTTP headers contain a line that looks like the following:
Server: Apache/1.3.33 (Unix) PHP/5.0.3 mod_ssl/2.8.16 OpenSSL/0.9.7c
With the expose_php directive enabled, the PHP version is not included in this header.
Of course, the .php file extension is a giveaway to visitors that PHP is in use on a website. If you want to use a totally different file extension, you need to first find the following line in httpd.conf:
AddType application/x-httpd .php
Then you need to change .php to any file extension you like. You can specify any number of file extensions, separated by spaces. To have PHP parse .html and .htm files so there is no indication that a server-side language is being used at all, you can use the following directive:
AddType application/x-httpd .html .htm
Safe Mode restricts filesystem access only to files owned by the script owner, and you can use the open_basedir directive to specify the directory in which a file must reside. If you specify a directory, PHP will refuse any attempt to access a file that is not in that directory or its subdirectory tree. The open_basedir directive works independently of Safe Mode.
To restrict filesystem access on your web server to only the /tmp directory, you use the following directive:
open_basedir = /tmp
Function Access Control
You can use the disable_functions directive to specify a comma-delimited list of function names that will be disabled in the PHP language. This setting works independently of Safe Mode.
To disable the dl function without turning on Safe Mode, you use the following directive:
disable_functions = dl
You can also disable access to classes by using the disable_classes directive in the same way.
You learned in Lesson 18, "Host Program Execution," how a malicious user might try to run an arbitrary host command on your system, and that you can use the escapeshellcmd function to prevent this kind of abuse.
A similar situation applies to database use through PHP. Suppose your script contains the following lines to execute a MySQL query based on a form value:
$sql = "UPDATE mytable SET col1 = " . $_POST["value"] . " WHERE col2 = 'somevalue'"; $res = mysql_query($sql, $db);
You are expecting $_POST["value"] to contain an integer value to update the value of column col1. However, a malicious user could enter a semicolon in the form input field, followed by any SQL statement he or she wants to execute.
For instance, suppose the following is the value of $_POST["value"]:
0; INSERT INTO admin_users (username, password) VALUES ('me', 'mypassword');
The SQL executed would then look like the following (the statements are shown here on separate lines for clarity):
UPDATE mytable SET col1 = 0; INSERT INTO admin_users (username, password) VALUES ('me', 'mypassword'); WHERE col2 = 'somevalue';
This is clearly a bad situation! The first statement updates the value of col1 for all rows in mytable. This will be an inconvenience, but the second statement creates a more serious problemthe user has been able to execute an INSERT statement that creates a new administrator login. The third statement is rubbish, but by the time the SQL parser reaches that statement and throws an error, the damage has been done. This type of attack is known as SQL injection.
Of course, for SQL injection to be a serious threat, the user must understand a little about your database structure. In this example, the attacker is aware that you have a table called admin_users, that it contains fields named username and password, and that the password is stored unencrypted.
A visitor to your website would not generally know such information about a database you built yourself. However, if your website includes open-source componentsperhaps you have used a freeware discussion board programthe table definitions for at least some of your database are accessible to users.
Furthermore, if your script produces output whenever a query fails, this could reveal important details about your database structure. On a production website, you should consider setting display_errors to off and using log_errors to write warnings and error messages to a file instead.
To prevent the possibility of a SQL injection attack, you must ensure that user-submitted data that forms part of a query cannot be used to interrupt the SQL statement that you intend to execute.
The previous example shows an integer value being updated. If this were a string value enclosed in single quotes, the attacker would need to submit a closing quote before the semicolon and then the SQL statement. However, when magic_quotes_gpc is turned on, a quotation mark submitted via a web form will be automatically delimited.
To be absolutely sure that form-submitted values are not vulnerable to SQL injection attacks, you should always ensure that the data received is appropriate. If your query expects a numeric value, you should test the form value with is_numeric or use settype to convert it to a number, removing any characters that are designed to fool SQL.
If you are working with several user-submitted values in one SQL statement, you can use the sprintf function to build a SQL statement string, using format characters that indicate the data type of each value. The following is an example:
$sql = sprintf("UPDATE mytable SET col1 = %d WHERE col2 = '%s'", $_POST["number"], mysql_escape_string($_POST["string"]));
The preceding example assumes that a MySQL database is being used, so the string value is passed to mysql_escape_string. For other databases, you should ensure that quote characters are adequately delimited, by using addslashes or another suitable method.