| PHP Security Measures Software Evaluation Andre S. Burton February 24, 2003 |
||||||||||
|
Introduction
Despite its inherent advantages, like any software, it comes with bugs and weaknesses. Whether you are an administrator or programmer, security should be a priority as 'hackers' are looking for ways to exploit the weaknesses of both the system and application code. As with most web programming software, security measures must be examined at the three possible levels of vulnerability: actual software or engine, system/administrator, and/or user/programmer. Software-Level Reliability, for the most part, is largely affected by an application's security flaws and usefulness/ease of use. In PHP's case, the two seem inversely proportional; trade-off must be made. Approaching the problem top-down, we see the problems start from the software-level - inherent software capability - and move down into the lower tier - the programmer's ability to create secure code. Let's start at the software-level. PHP's website, http://php.net, always posts vulnerability findings and solutions to those findings in a timely manner. The latest vulnerability advisory, exposes vulnerabilities in versions 4.2.0 and 4.2.1 for all platforms (http://www.php.net/release_4_2_2.php). According to this advisory, "An intruder may be able to execute arbitrary code with the privileges of the web server. This vulnerability may be exploited to compromise the web server and, under certain conditions, to gain privileged access." - privileged access system administrators have nightmares about. Many times as a programmer, administrator or privileged access is something not afforded to you as a user of the software- sometimes for your own good. Although new vulnerabilities are exposed and fixes are posted, you have little control over how or when the problems will be fixed. As a security-aware programmer, it may be in your best interest to contact your system administrator and make 'kind' suggestions and/or recommendations for change; recommendations that are at the mercy and decision of the people who run the system. System-Level As a semi-admin, I do not want to fix the problem by editing the source code. Instead, I find it easier in many cases to restrict or limit what features are available, and with PHP, this is easily done with configuration. There are commonly recommended values for certain configuration options, but before I talk about those, it would be more useful to look at what types of security mistakes programmers make. Programmer-Level Global Variables The tag <input type="text" name="color"> renders:
Any data entered into this box once the form is submitted is converted into a PHP variable. Once we have that data in variable form, we can print it out, save it to a database, etc. In the example, we would reference that variable as $color, as the $ denotes a variable. If we wanted to print that variable out to the screen, <? would suffice. Now this assumes the form is submitted using the POST method (<form action="someprogram.php" method="POST">). What if a user of your program added a ?color=something_detrimental to the end of your URL (making it GET)? Adding variable name-value pairs or changing variable values is a common way to break ones code. This is especially the case when the variable is not validated. To give you a better example, let's look at an oversimplified case where we want to authenticate a user using the following code:
Here it is assumed the user will only use the program as intended. What if the user typed in: someprogram.php?authenticate=1 Now the user is authenticated and never needed to guess the password (even if SSL was used to encrypt the data). General rule: PHP scripts cannot trust any variable that has not been explicitly declared. PHP variables do not have to be declared; they are pretty much declared when they're being used. This is advantageous as programmers don't have to worry as much about data typing or the extra line of code. However, since variables are created as needed, validating variables becomes highly important as users can seemingly create any variable at any time. One common way to prevent GET variable creation on the user-end (for programmers) is to only accept variables found in the $HTTP_POST_VARS array. $HTTP_POST_VARS is an associative array meaning for each index, a literal value corresponds to a value assigned to a particular variable in the program. This is an environmental variable so it should be available to you as a programmer regardless of platform. If I only wanted the variable $authenticate to come from a 'POST' ed form, I would use like:
This of course is not the most efficient way to authenticate a user, but serves its purpose in showing how to get around the problem of accepting variables manipulated from the URL. We also want to validate variables based on their type...not just the way they were submitted. If you have to generate a database query based on user input, you want to make sure the input is of a certain type.
What if we wanted $user as a result of a GET request? In this case, a user can change the value for the $user variable within the URL. What if the user substituted this string for the $user variable: $user = "drop db database"; MySQL nested query capability is version dependent, but assuming the correct version, the variable $Query is now assigned: "select * from user where uname='drop db database'"; Although the entire query may be erroneous, nested queries start at the inner-most queries and execute them first. Assuming you have drop database capability, dropping the entire database will cause you, the programmer, to lose all your data - probably forever. Place restrictions on the variable. Knowing that $user is between 8 to 14 characters long and contains no spaces or special characters like %, can be the basis for validation.
File handling include("include.php"); is commonly used to include class or reusable function definitions. Include basically takes the contents of include.php and text between PHP tags as PHP code. What if the included file was from another server - include("http://somehost.com/somefile.php")? Big risk. You are basically assuming that the code at that location will always be trustworthy and secure; not a good assumption to make given cost-benefit analysis. Somebody could have easily replaced that file with a PHP program to break yours... fpassthru($FP) simply renders all text in $FP as text. Nothing between PHP tags will be rendered as code. Allowing a user to put a PHP file on your server in this function, would allow him to see code unrendered. Here, he can attack further vulnerabilities of any given program at will. Similiarly, file extensions can be problematic. Pre-PHP4 days, many programmers used different extensions for PHP files: .php3, .php4, etc., although this is less of a problem nowadays. Putting the version number at the end of the extension made code version dependent. Compiled PHP is only given files that are recognized as PHP code; therefore, a PHP file with a version-specific extension on a platform only supporting or recognizing current version extensions may render files as plain text. Once again, code is exposed unrendered (although not necessarily executable). Common mistake: do not give included files extensions that will not hide the code in a web-accessible directory. In my personal web-directory, let's assume I have a file called include.inc. In this file, I have functions I wish to reuse in various parts of other programs. Including this file as includes or requires, I now have multiple files dependent on a file whose code can be read by any user. A user can now take this code and exploit its vulnerabilities in other programs that use it. System Execution system("less dbConnection.php"); The user now has access to the contents of a PHP program that can execute. A greater threat: what if he wanted to see other private server data? system("less /etc/passwd"); As harmless as less or more seem, they can be detrimental to the system's security should the wrong files be exposed. Obviously, less is not the only command that can prove useful. As a system admin, there are ways to reduce the number of executable commands using a configuration option called safe_mode; we'll talk about that in the next section. Quick note: as a programmer are not completely blind to your system's setup. Create a PHP file in a web-accessible directory on your desired host to look at how your host configured PHP by inserting this code:
See this example running on this server. Back to System-Level Configuration Set register_globals off. The most recommended option. No longer can users switch between HTTP methods. However, it hurts PHP's simplicity. Set safe_mode on. Does a number of things when turned on: "1. restrict the commands executed by system or exec, 2. restrict which functions can be used, 3. restricts file access based on ownership of script and target file, and 4. kills upload completely." [2] Installation If you want PHP installed as a web module, you're going to have to install it with Apache. If you already have Apache installed, you're going to have to reinstall it - it's worth it, don't worry. Here is the basic Linux install for PHP with Apache and MySQL (assuming it's already installed) including the above mentioned 'secure' configuration options:
Not as easy as it seems. I had a MySQL client installed... Resources General
and Code Repositories Security Books Reference
|