As a powerful language, PHP can be installed in either a module or CGI. its interpreter can access files, run commands, and create network connections on the server. These functions may add many insecure factors to the server. you must install and configure PHP correctly and write secure code.
I. CGI mode installation security
II. install security using the Apache Module
When PHP is installed as an Apache module, it inherits the permissions of Apache users (usually "nobody. This has some impact on security and authentication. For example, if you use PHP to access the database, unless the database has its own access control, the "nobody" user must be able to access the database. This means that malicious scripts may access and modify the database without providing the user name and password. A web Spider may also accidentally discover the database management page and delete all databases. You can avoid this problem through Apache authentication, or use LDAP,. htaccess, and other technologies to design your own anti-question model, and use the code as a part of the PHP script.
A common security-negative error is to give Apache root permissions or bind Apache with more powerful functions through other channels.
It is extremely dangerous to escalate Apache user permissions to root, which may compromise the security of the entire system. Therefore, unless you are a security expert, you must never use su, chroot, or run as root.
In addition, there are some simple solutions. For example, open_basedir can be used to limit which directories can be used by PHP. You can also set the exclusive region of Apache to limit all web activities to non-user and non-system files.
III. file system security
PHP complies with the security mechanism for file and directory permissions in most server systems. This allows the administrator to control which files are readable in the file system. You must pay special attention to the global readable files and ensure that each user with permissions can read these files securely.
PHP is designed to access the file system at the user level, so it is entirely possible to write a piece of PHP code to read system files such as/etc/passwd, change the network connection and send a large number of print tasks. Therefore, make sure that PHP code is suitable for reading and writing files.
Two important measures to prevent such problems.
# Only PHP web users have limited permissions.
# Check all submitted variables.
$ Username = $ _ SERVER ['remote _ user']; // use the authentication mechanism $ userfile =$ _ POST ['User _ submitted_filename ']; $ homedir = "/home/$ username"; $ filepath = "$ homedir/$ userfile"; if (! Ctype_alnum ($ username) |! Preg_match ('/^ (? : [A-z0-9 _-] | \.(?! \.) + $/ID ', $ userfile) {die ("Bad username/filename ");}
* Null characters
Because PHP file system operations are based on C-language functions, it may handle Null characters in unexpected ways. The Null character is used in the C language to mark the end of a string. a complete string starts with it and ends with a Null character.
# Code that will be attacked by Null characters:
$file = $_GET['file']; // "http://www.cnblogs.com/etc/passwd\0"if (file_exists('/home/wwwrun/'.$file.'.php')) { // file_exists will return true as the file /home/wwwrun/http://www.cnblogs.com/etc/passwd exists include '/home/wwwrun/'.$file.'.php'; // the file /etc/passwd will be included}
# Verify correct input practices
$ File = $ _ GET ['file']; // checks the white list of strings. switch ($ file) {case 'main': case 'foo ': case 'bar': include '/home/wwwrun/include /'. $ file. '. php'; break; default: include '/home/wwwrun/include/main. php ';}
IV. database security
* Design a database
The first step is generally to create a database, unless you use a third-party database service. When creating a database, an owner is specified to execute and create statements. Generally, only the owner (or super user) has the right to perform any operation on objects in the database. If you want other users to use it, you must grant them permissions.
Applications should never use the database owner or superuser account to connect to the database, because these accounts can perform any operations, such as modifying the database structure (such as deleting a table) or clear the content of the entire database.
You should create different database accounts for each aspect of the program and grant extremely limited permissions to database objects. Assign only the permissions required to complete the functions of a user, so that the same user can complete the tasks of another user. In this way, even if attackers exploit a program vulnerability to gain database access permissions, they can only achieve the same impact scope as the program.
Do not use web applications (user scripts) to implement all Transaction logic. It is best to use a view, trigger, or rule at the database level. When the system is upgraded, you need to open up new interfaces for the database, and then you must redo all the database clients. In addition, triggers can process fields transparently and automatically, and provide useful information in real time for debugging programs and tracking.
* Connect to the database
Setting up a connection to the SSL encryption technology can increase the communication security between the client and the server, or use SSH to encrypt connections between the client and the database. If these technologies are used, it is very difficult for attackers to monitor server communication or obtain database information.
* Encrypted storage model
SSL/SSH can protect the data exchanged between the client and the server, but SSL/SSH cannot protect the existing data in the database. SSL is just a protocol used to encrypt network data streams.
If attackers obtain a permit to directly access the database (bypassing the web server), sensitive data may be exposed or abused unless the database itself protects the information. Data encryption in databases is an effective way to reduce such risks, but only a few databases provide these encryption functions.
A simple solution to this problem is to create your own encryption mechanism and use it in a PHP program. PHP has several extension libraries to do this, such as Mcrypt and Mhash. they contain multiple encryption algorithms. The script encrypts the data before it is inserted into the database, and then decrypts the data when it is extracted.
If some truly hidden data does not need to exist in plain text (that is, it does not need to be displayed), you can use the hash algorithm. The most common example of using the hash algorithm is to store the hash encrypted by MD5 in the database to replace the original plaintext password. See crypt () and md5 ().
* SQL injection
Many web developers have not noticed that SQL queries can be tampered with, so they regard SQL queries as trustworthy commands. However, SQL queries can bypass access control and bypass authentication and permission checks. Furthermore, it is possible to run the host OS-level commands through SQL queries.
Direct SQL command injection is a common technique used by attackers to create or modify existing SQL statements, so as to obtain hidden data or overwrite key values, even the purpose of executing database host operating system commands. This is achieved through applications that obtain user input and combine static parameters into SQL queries. The following are some examples.
Attackers can create a new super user in the database because they lack the authentication of input data and use superusers or other database accounts with the permission to create new users to connect.
Example #1 code for paging data ...... It can also be used to create a super user (PostgreSQL system ).
<? Php $ offset = $ argv [0]; // note that no verification is entered! $ Query = "SELECT id, name FROM products order by name LIMIT 20 OFFSET $ offset;"; $ result = pg_query ($ conn, $ query);?>
Generally, users will click the "previous page" and "next page" links of the $ offset value. The original code only considers $ offset as a numerical value. However, if someone tries to urlencode () the following statement first and then add it to the URL:
0;
Insert into pg_shadow (usename, usesysid, usesuper, usecatupd, passwd)
Select 'crack', usesysid, 'T', 'T', 'crack'
From pg_shadow where usename = 'second s ';
-- Then he can create a Super User. Note that the value 0 is only used to provide a correct offset to supplement the original query, so that no error occurs.
Note:
-- Is the annotation mark of SQL, which can be used to tell the SQL interpreter to ignore subsequent statements.
It is a feasible way to get a password for the page that displays the search result. What attackers need to do is to find out which submitted variables are used for SQL statements and handle them improperly. These variables are usually used in SELECT query condition statements, such as WHERE, order by, LIMIT, and OFFSET. If the database supports UNION construction, attackers may also attach a complete SQL query to the original statement to obtain the password from any data table. Therefore, encryption of password fields is very important.
Example #2 show articles ...... And some passwords (any database system)
The code is as follows:
<? Php
$ Query = "SELECT id, name, inserted, size FROM products
WHERE size = '$ size'
Order by $ order LIMIT $ limit, $ offset ;";
$ Result = odbc_exec ($ conn, $ query );
?>
You can add another SELECT query based on the original query to obtain the password:
'
Union select '1', concat (uname | '-' | passwd) as name, '2017-01-01 ', '0' from usertable;
-- If the preceding statements (using 'and --) are added to any variable in $ query, this is troublesome.
SQL UPDATE is also under attack. Such queries may also be inserted or appended to another complete request as in the preceding example. However, attackers prefer the SET clause so that they can change some data in the data table. In this case, you must know the database structure before the query can be modified successfully. You can use the variable name in the form to guess the field or perform brute-force cracking. There are not many naming methods for fields that store user names and passwords.
Example #3 reset the password ...... To get more permissions (any database system)
The code is as follows:
<? Php
$ Query = "UPDATE usertable SET pwd = '$ pwd' WHERE uid =' $ uid ';";
?>
However, malicious users submit 'or uid like' % admin % '; -- as the variable value to $ uid to change the admin password, you can also submit the value of $ pwd as "hehehe", admin = 'yes', trusted = 100 "(with a space later) to obtain more permissions. In this case, the query statement is actually changed:
The code is as follows:
<? Php
// $ Uid = 'or uid like' % admin % ';--
$ Query = "UPDATE usertable SET pwd = '...' WHERE uid ='' or uid like '% admin % ';--";
// $ Pwd = "hehehe ', admin = 'yes', trusted = 100"
$ Query = "UPDATE usertable SET pwd = 'hehei', admin = 'yes', trusted = 100 WHERE
...;";
?>
The following terrible example shows how to execute system commands on some databases.
Example #4 attack the operating system of the host where the database is located (MSSQL Server)
The code is as follows:
<? Php
$ Query = "SELECT * FROM products WHERE id LIKE '% $ prod % '";
$ Result = mssql_query ($ query );
?>
If the attacker submits a % 'exec master.. xp_mongoshell 'net user test testpass/add' -- as the value of the variable $ prod, $ query will become
The code is as follows:
<? Php
$ Query = "SELECT * FROM products
WHERE id LIKE '% a %'
Exec master.. xp_mongoshell 'net user test testpass/add '--";
$ Result = mssql_query ($ query );
?>
The MSSQL server executes this SQL statement, including the command used to add users to the system. If the program runs with sa and the MSSQLSERVER service has sufficient permissions, attackers can obtain a system account to access the host.
Note:
Although the above example is for a specific database system, it does not mean that similar attacks cannot be carried out against other database systems. Different methods may cause various databases to suffer.
Preventive actions
Some may comfort themselves by saying that the attacker must know the database structure information before carrying out the above attacks. That's right. However, no one can ensure that attackers will not be able to obtain the information. once they get the information, the database may be leaked. If you are using open-source software packages to access the database, such as Forum programs, attackers can easily get the relevant code. If these codes are poorly designed, the risk is even greater.
These attacks are always based on code that lacks security awareness. Therefore, never trust external input data, especially from clients, including selection boxes, form hidden fields, and cookies. As in the first example above, even normal queries may cause disasters.
• Never use a superuser or owner account to connect to a database. Use accounts with strictly limited permissions.
• Check whether the input data has the expected data format. PHP has many functions that can be used to check input, from simple variable functions and character type functions (such as is_numeric (), ctype_digit ()) this can be done by using complex Perl-compatible regular expression functions.
• If the program is waiting to enter a number, you can use is_numeric () to check the number, or directly use settype () to convert its type, or use sprintf () format it as a number.
Example #5 a safer paging method
The code is as follows:
<? Php
Settype ($ offset, 'integer ');
$ Query = "SELECT id, name FROM products order by name LIMIT 20 OFFSET $ offset ;";
// Note the % d in the format string. If % s is used, it will be meaningless.
$ Query = sprintf ("SELECT id, name FROM products order by name LIMIT 20 OFFSET % d ;",
$ Offset );
?>
• Use Database-specific sensitive character escape functions (such as mysql_escape_string () and SQL _escape_string () to escape non-numeric data submitted by the user. If the database does not have a special sensitive character escape function, addslashes () and str_replace () can be used in place to complete this task. Look at the first example. This example shows that it is not enough to enclose the static part of the query with quotation marks, and the query is easily broken.
• Do not show any confidence in the database, especially the database structure. See error reports and error handling functions.
• You can also use features such as database stored procedures and predefined pointers to abstract database access, so that users cannot directly access data tables and views. However, this method has other influences.
In addition, it is also a good way to save query logs by using code or database systems when permitted. Obviously, logs cannot prevent any attacks, but they can be used to track which program has been attacked. The log itself is useless. you must check the information contained in the log. After all, more information is better than nothing.
V. Error Report
For PHP Security, error reporting is a double-edged sword. On the one hand, it can improve security and on the other hand, it is harmful.
The common method used to attack the system is to input incorrect data and view the error message type and context. This helps attackers collect server information to find vulnerabilities. For example, if an attacker knows the form information on a page, he will try to modify the variable:
Example #1 use custom HTML page attack variables
The code is as follows:
Generally, the error prompt returned by PHP can help the developer debug the program. it will indicate which functions or code of the file has an error and the number of lines in the file where the error occurs, these are the information that PHP can provide. Many PHP developers use the show_source (), highlight_string (), or highlight_file () functions to debug the code. However, on the official website, this method may expose hidden variables, unchecked syntaxes, and other information that may compromise system security. It is dangerous to run some programs with internal debugging processing, or use general debugging technology. If attackers determine which specific debugging technology the program uses, they will try to send variables to enable the debugging function:
Example #2 use variables to enable the mode function
The code is as follows:
Regardless of the error handling mechanism, the ability to detect system errors will provide more information to attackers.
For example, the unique error prompt style of PHP indicates that the system is running PHP. If attackers are looking for. html is a page. if you want to know the background technology (in order to find system vulnerabilities), they will submit the wrong data, and then they will know that the system is based on PHP.
A function error may expose the database being used by the system, or provide attackers with useful information about webpages, programs, or designs. Attackers often find open database ports and some bugs or vulnerabilities on the pages. For example, attackers can make program errors with some abnormal data to detect the authentication sequence in the script (the row number indicated by the error message) and other information that may be leaked in the script.
A file system or PHP error exposes the permissions of the web server and the organizational structure of the file on the server. The error code written by the developer will aggravate this problem and cause the original hidden information to be leaked.
There are three common solutions to these problems. The first is to thoroughly check all functions and try to make up for most errors. The second is to completely disable error reports for online systems. The third is to use the PHP custom error handling function to create its own error handling mechanism. Based on different security policies, all three methods may apply.
One way to prevent this problem in advance is to use error_reporting () to help make the code safer and find the danger of using variables. Open the E_ALL test code before releasing the program, so that you can quickly find out where the variable is used improperly. Once you prepare for official release, set error_reporting () to 0 to close the error report completely or set php. set display_errors in ini to off to disable all error display to isolate code from detection. Of course, if you want to do this later, do not forget to open the log_errors option in the ini file, and specify the file used to record error information through error_log.
Example #3 use E_ALL to find dangerous variables
The code is as follows:
<? Php
If ($ username) {// Not initialized or checked before usage
$ Good_login = 1;
}
If ($ good_login = 1) {// If above test fails, not initialized or checked before usage
Readfile ("/highly/sensitive/data/index.html ");
}
?>
6. use Register Globals
Maybe the most controversial change in PHP is that the default value of register_globals in the configuration file is changed from on to off from PHP» 4.2.0. The dependency on this option is so common that many people do not know its existence and think that PHP is working like this. This section explains how to use this command to write insecure code, but you must know that this command is not safe and misuse it.
After register_globals is enabled, various variables are injected with code, such as request variables from HTML forms. In addition, PHP does not require initialization before using variables, which makes it easier to write insecure code. This is a tough decision, but the PHP community decided to disable this option by default. When you open a variable, people do not know where it comes from when using it. However, the close of register_globals changes the bad situation where the internal variables of this code are mixed with the variables sent by the client. The following is an example of using register_globals incorrectly:
Example #1 Example of register_globals = on error
The code is as follows:
<? Php
// If the user is valid, the value $ authorized = true is assigned.
If (authenticated_user ()){
$ Authorized = true;
}
// Because $ authorized is not initialized to false in advance,
// When register_globals is enabled, it may use GET auth. php? Authorized = 1 to define the variable value
// Anyone can bypass authentication.
If ($ authorized ){
Include "/highly/sensitive/data. php ";
}
?>
When register_globals = on, the above code is dangerous. If it is off, $ authorized cannot be changed through URL request or other methods, which is much better, even though initialization variables are a good programming habit. For example, if $ authorized = false is added before the preceding code is executed, it can be used whether register_globals is on or off, because the user status is initialized to unauthenticated.
Another example is about session. When register_globals = on, $ username can also be used in the following code, but you must realize that $ username may also come in through other methods, such as GET Through URL.
Example #2 examples of compatibility with register_globals on and off when using Sessions
The code is as follows:
<? Php
// We do not know the source of $ username, but it is clear that $ _ SESSION is
// Source session data
If (isset ($ _ SESSION ['username']) {
Echo "Hello{$ _ SESSION ['username']}";
} Else {
Echo "HelloGuest
";
Echo "wocould you like to login? ";
}
?>
It is entirely possible to take appropriate preventive measures so that a warning is given when a variable input is forged. If you know exactly where the variable comes from, you can check whether the submitted data is submitted from an improper form. However, this does not ensure that the variables are not forged, which requires attackers to guess how to forge them. If you do not care about the REQUEST data source, you can use the $ _ REQUEST array, which includes all the data of GET, POST, and COOKIE. For more information, see variables outside of PHP in this manual.
Example #3 detect harmful variables
The code is as follows:
<? Php
If (isset ($ _ COOKIE ['Magic _ cooker']) {
// MAGIC_COOKIE from cookie
// This ensures that the data is from the cookie.
} Elseif (isset ($ _ GET ['Magic _ cookier']) | isset ($ _ POST ['Magic _ cookier']) {
Mail ("admin@example.com", "Possible breakin attempt", $ _ SERVER ['remote _ ADDR ']);
Echo "Security violation, admin has been alerted .";
Exit;
} Else {
// The MAGIC_COOKIE variable is not set in this request.
}
?>
Of course, simply disabling register_globals does not mean that all the code is safe. For each piece of submitted data, you must perform a specific check on it. Always verify user data and initialize variables! Set error_reporting () to E_NOTICE to check uninitialized variables.