Other Security Features
In addition to Safe Mode, PHP provides a number of functions that allow you to place restrictions on the features available to PHP.
Hiding 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:
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:
expose_php = On
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:
Server: Apache/1.3.33 (Unix) PHP/5.0.3 mod_ssl/2.8.16
OpenSSL/0.9.7c
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 l and 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 .php
AddType application/x-httpd l
Filesystem Security
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:
You can also disable access to classes by using the disable_classes directive in the same way.
disable_functions = dl
Database Security
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:
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"]:
$sql = "UPDATE mytable SET col1 = " . $_POST["value"] . "
WHERE col2 = 'somevalue'";
$res = mysql_query($sql, $db);
The SQL executed would then look like the following (the statements are shown here on separate lines for clarity):
0; INSERT INTO admin_users (username, password)
VALUES ('me', 'mypassword');
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.
UPDATE mytable SET col1 = 0;
INSERT INTO admin_users (username, password)
VALUES ('me', 'mypassword');
WHERE col2 = 'somevalue';
| Database Permissions It is vital that the database connection from your script be made by a database user who has only just enough access rights to perform the job.You should certainly never connect as an administrator from a script. If you did, an attacker would be able to gain full access to your database and others on the same server. Attackers will also be able to run the GRANT or CREATE USER command to give themselves full access outside the confines of your script! |
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.
$sql = sprintf("UPDATE mytable SET col1 = %d
WHERE col2 = '%s'",
$_POST["number"],
mysql_escape_string($_POST["string"]));