142 Chapter 4 Web Application Concepts Executing System Commands Pay special attention to security when working with files or executing system commands. Imagine a typical source-viewer script that takes a filename as argument and displays the file in colored mode: show_source($file); You intend the script to be called with script.php3?file=script.php3, but what if somebody calls it with script.php3?file=/etc/passwd? Right you have a problem because you trusted variables coming from the Web to be in a certain range (for example, the current directory). It s absolutely necessary to enforce such assumptions on the server, for example by using this code snippet instead: show_source(basename($file)); Let s look at another example, a directory viewer that we found on the Web. (Listing 4.3 shows a slightly modified and shortened version.) The author, Marcus Xenakis, kindly gave us permission to include it here. Listing 4.3 Directory browsing with security risks. print(
); exec( ls -la $dir , $lines, $rc); $count = count($lines) - 1; for ($i = 1; $i <= $count; $i++) { $type = substr($lines[$i], 0, 1); $name = strrchr($lines[$i], ); $name = substr($name, 1); $dire = substr($lines[$i], 0, strpos($lines[$i], $name)); printf( , ($type == d) ? blue : black ); print( $dire ); if ($type == d ) { if ($name == . or $name == .. ) { print( $name ); } else { printf( $name , .empty($dir) ? $name : $dir/$name ); } } else { printf( $name , .empty($dir) ? $name : $dir/$name ); } } print(
);
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Inexpensive Web Hosting services
Security Considerations 141 Security has to be taken into consideration in the very first step of using a scripting language its installation.As you know,PHP can be installed as a server module (Apache module, ISAPI/NSAPI plug-in) or as stand-alone CGI program. If you install it as a server module, it s part of the underlying Web server and inherits its security.There are no known PHP-specific attacks for this kind of setup. Of course, you still need to have a secure server, but that s too broad a topic to cover here. CGI programs, on the other hand, have been famous for a wide variety of possible attacks both intrusion and denial-of-service attacks.Traditionally,the most serious problems arise when putting a script interpreter into the cgi-bin directory of a Web server.With some script interpreters,users would be able to execute any command through it. PHP tries to prevent some of these attacks. If invoked from the Web, it discards command-line parameters passed by the CGI interface requests such as http://server.com/cgi-bin/php?etc/passwd will fail. Unfortunately, another attack directly related to flaws in the CGI specification is still possible:You can access any file below the Web server s document root, even if the directory is protected by HTTP-AUTH (an .htaccess file), just by calling through via the PHP interpreter: www.server.com/cgi-bin/php/secret-directory/file.html would allow you to view file.html even if secret-directory is protected by an .htaccess file. If you re using Apache, enable –enable-force-cgi-redirect when compiling PHP to avoid this problem. Please refer to the installation section in the PHP manual for more detailed information on this topic. Of course,choosing a secure installation is only the first step.A good programmer will keep an eye on security throughout the development process.There are so many possible risks that we can t possibly cover them all within this chapter. Instead, we ll try to give you some generally applicable advice. Don t Trust the Web All data coming from the Web is insecure and should be validated by your application. For example, there s no guarantee that your script is invoked from its associated form interface users could bypass the HTML form and call the script directly, possibly specifying parameters via their own GET or POST. Validating form data is one of the most tedious tasks a Web application developer has to do in his or her daily work. Basic checks can be automated with the routines of the library featured in Chapter 5, Basic Web Application Strategies. For more complicated validation,we haven t found a generic way yet so we do it manually.Such scripts follow the typical logic outlined in Chapter 5, which we call the PHP Normal Form.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Inexpensive Web Hosting services
140 Chapter 4 Web Application Concepts you keep the documented differences in mind, it should be possible to use our library as a drop-in replacement of the PHP 4.0 session functions.The full source code can be found on the CD-ROM. Our port can also help you to understand in detail how the PHP internal session library works.The session_start() function, for example, mirrors the original C function very closely. Security Considerations Example 1. In early 1999, we performed a Web site audit for a leading online job database.During the analysis,we discovered a security issue that left us speechless.The Web site had an associated online shop where visitors could buy books related to the topic of job hunting.The shop had been developed by an independent contractor who had already built several other online stores based on his Perl scripts and had made available on his Web site a demo including significant parts of the source code. Each time a visitor ordered an item, a plaintext file was generated, containing all order information,even credit card data all unencrypted.And not only that,the files were stored on a publicly visible directory on the Web server for the convenience of the Web site maintainers, as he told us.This directory was protected only by the fact that it was declared as not browseable in Apache s configuration. Because the filenames followed a standard naming convention in the form yyyy-dd-mm-hh-mm-ss.txt, it would have been a no-brainer for a hacker to write a script to search for files. Example 2. Network Solutions, for a long time the only domain registrar, had the idea in September 1999 to give a free Web mail account to their premium customers. They created a username and a password for each account and mailed it to the respective customer.The username consisted of the customer s last name ( doe ) and the password was the same as the username except that a trailing nsi ( doensi ) was added! For over 24 hours you could log into the system by entering other customers passwords. You could change the password, read mail sent to that account, and even send mail in the name of that customer. Both security problems were caused not by flaws inherent in the programming language used, but by improper programming. PHP itself is very secure we have never heard of holes like ASP s ::$DATA bug.This issue, discovered in June 1998, allowed any user to view the source code of ASP scripts over the Web, simply by appending the string ::$DATA to the file s URL (for example, www.server.com/script.asp::$DATA) a software developer s nightmare.An earlier Windows-only security issue allowed you to access the source by specifying a URL like www.server.com/script.asp. (note the trailing dot), and after the ::$DATA bug, on some versions of Personal Web Server for Windows you could use www.server.com/……/ to get access to the entire hard disk, not just the Web server s document root tree.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost PHP Web Hosting services
138 Chapter 4 Web Application Concepts Listing 4.2 Continued $result = mysql_db_query($sess_mysql[ db ], REPLACE INTO . .$sess_mysql[ table ]. VALUES ( $sess_id , $val , null) ) .or die(mysql_error()); return(true); } function sess_mysql_destroy($sess_id) { global $sess_mysql; // Delete from the MySQL table all data for the session $sess_id $result = mysql_db_query($sess_mysql[ db ], DELETE FROM . .$sess_mysql[ table ]. WHERE id = $sess_id ) or die(mysql_error()); return(true); } function sess_mysql_gc($max_lifetime) { global $sess_mysql; // Old values are values with a Unix less than now - $max_lifetime $old = time() - $max_lifetime; // Delete old values from the MySQL session table $result = mysql_db_query($sess_mysql[ db ], DELETE FROM . .$sess_mysql[ table ]. WHERE UNIX_TIMESTAMP(t_stamp) < $old ) or .die(mysql_error()); return(true); } /* * Basic Example: Registering above functions with session_set_save_handler() * $foo = 10; session_set_save_handler( sess_mysql_open , , sess_mysql_read , . sess_mysql_write , sess_mysql_destroy , sess_mysql_gc ); session_start(); session_register( foo ); echo foo: $foo ; $foo++; * */
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost PHP Web Hosting services
HTTP and Sessions 139 Page Caching The session library also allows you to control how pages are cached.This is done via the HTTP Cache-Control directives. In the PHP configuration, the cache_limiter directive can be set to nocache, private, or public.As discussed in Chapter 6, this is very similar to the behavior of the PHPLib (but note that the PHPLib uses just no instead of nocache). Page caching is set to nocache by default.This prevents caching at all,and is also the standard behavior of all PHP pages, as you may know. For dynamically generated pages, this is usually the preferred method, since these pages will often differ from request to request. However, you may want to rethink this strategy for certain parts of your application that don t change often your server hardware will thank you for it. The output header will look like this: Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-cache Pragma: no-cache Setting cache_limiter to private will allow browsers to cache the pages, but not proxies or other gateway applications. Note that this differs from the proxy-revalidate directive; in the latter case, the proxy is allowed to keep the content of the page to issue a revalidation instead of a full retrieval.The generated HTTP headers will look similar to these: Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: private, max-age=10800 Last-Modified: Thu, 03 Feb 2000 15:56:11 GMT The last possible value, public, allows full caching by both the client and proxies. Be careful when using the public cache option: Pages generated with this setting may be available to third-party users who have access to proxies. When using public caching, the cache_expire PHP configuration directive will be taken into account.This directive specifies the number of seconds after which the cache will expire.The generated headers could look like this: Expires: Thu, 03 Feb 2000 18:56:11 GMT Cache-Control: public, max-age=10800 Last-Modified: Thu, 03 Feb 2000 15:56:11 GMT PHP 3.0 Sessions Because the PHP 4.0 sessions API is nice and intuitive to use, we wanted to have that for PHP 3.0, too. Having a consistent session interface for both versions would be great, we thought and for some small projects you just don t want to use the PHPLib. So we had a closer look at sessions.c and ported it over to native PHP 3.0. While we didn t preserve some internals, such as the algorithm used for the generation of the session ID, we tried to make the API 100% compatible to PHP 4.0. Some limitations obviously do exist:Automatic URL rewriting isn t available,for example.But if
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost PHP Web Hosting services
136 Chapter 4 Web Application Concepts The functions are defined as follows: n bool open(string save_path, string sess_name) This function is executed on the initialization of a session; you should use it to prepare your functions, to initialize variables, or the like. It takes two strings as arguments.The first is the path where sessions should be saved.This variable can be specified in php.ini or by the session_save_path() function you can use this variable as a wild card and use it for module-specific configuration.The second argument is the session s name, by default PHPSESSID.The open() function should return true on success and false on error. n bool close() This function is executed on shutdown of a session. Use it to free memory or to destroy your variables. It takes no arguments and should return true on success and false on error. n mixed read(string sess_id) This important function is called whenever a session is started. It must read out the data of the session identified with sess_id and return it as a serialized string. If there s no session with this ID, an empty string should be returned. In case of an error, false should be returned. n bool write(string sess_id, string value) When the session needs to be saved,this function is invoked.The first argument is a string containing the session s ID; the second argument is the serialized representation of the session variables.This function should return true on success and false on error. n bool destroy(string sess_id) When the developer calls session_destroy(), this function is executed. It should destroy all data associated with the session sess_id and return true on success and false on error. n bool gc(int max_lifetime) This function is called on a session s startup with the probability specified in gc_probability. It s used for garbage collection; that is, to remove sessions that weren t updated for more than gc_maxlifetime seconds.This function should return true on success and false on error. The example in Listing 4.2 puts the user callback functions into action, defining a storage module to save session data to a MySQL database. (The full example, including the necessary MySQL table schema, is included on the CD-ROM accompanying this book.) Because session_set_save_handler() currently accepts only simple functions and no class functions, we ve used good old structural programming instead of a class. Because inheritance or multiple instances wouldn t make sense for this type of code anyway, it s not a big loss.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Inexpensive Web Hosting services
HTTP and Sessions 137 Listing 4.2 A MySQL storage module for the PHP 4.0 session library. $sess_mysql = array(); $sess_mysql[ open_connection ] = true; .// Establish a MySQL connection on session startup? $sess_mysql[ hostname ] = localhost ; // MySQL hostname $sess_mysql[ user ] = root ; // MySQL username $sess_mysql[ password ] = ; // MySQL password $sess_mysql[ db ] = book ; // Database where to store the .sessions $sess_mysql[ table ] = sessions ; // Table where to store the .sessions function sess_mysql_open($save_path, $sess_name) { global $sess_mysql; // Establish a MySQL connection, if $sess_mysql[ open_connection ] is true if ($sess_mysql[ open_connection ]) { $link = mysql_pconnect($sess_mysql[ hostname ], $sess_mysql[ user ], .$sess_mysql[ password ]) or die(mysql_error()); } return(true); } function sess_mysql_read($sess_id) { global $sess_mysql; // Select the data belonging to session $sess_id from MySQL session table $result = mysql_db_query($sess_mysql[ db ], SELECT data FROM . .$sess_mysql[ table ]. WHERE id = $sess_id ) or die(mysql_error()); // Return an empty string if no data was found for this session if(mysql_num_rows($result) == 0) { return( ); } // Session data was found, so fetch and return it $row = mysql_fetch_array($result); mysql_free_result($result); return($row[ data ]); } function sess_mysql_write($sess_id, $val) { global $sess_mysql; // Write the serialized session data ($val) to the MySQL session table continues
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Inexpensive Web Hosting services
HTTP and Sessions 135 Storage Modules To read and save session data, PHP uses storage modules, thus abstracting the back end of the library. Currently, three storage modules are available: files, mm, and user.By default, PHP uses the files module to save the session data to disk. It creates a text file named after the session ID in /tmp. In the previous example, the content of this file would look like this, which is a serialized representation of the variable: counter|i:4; You probably won t ever need to access this file directly. If you need higher performance, the mm module is a viable alternative; it stores the data in shared memory and therefore isn t limited by the hardware I/O system.The last module, user, is used internally to realize user-level callback functions that you define with session_set_save_handler(). The real power lies in the capacity to specify user callbacks as storage modules. Because you can write your functions to handle sessions while still being able to rely on the standardized PHP API, you can store sessions wherever and however you want in a database like MySQL, XML files, on a remote FTP server (okay, the latter wouldn t make much sense, but you get the idea). The function session_set_save_handler() takes six strings as arguments, which must be your callback functions.The syntax of the function is as follows: void session_set_save_handler(string open, string close, string read, .string write, string destroy, string gc) Serializing Data Serializing means the transformation of variables to a byte-code representation that can be stored anywhere as a normal string. Without this feature, it wouldn t be possible, for example, to store PHP arrays into a database. Serializing data is very useful for preserving data across requests an important facet of a session library. You can use serialize() and deserialize(), but note that in PHP 3.0 these functions don t work correctly on objects (classes); class functions will be discarded. Excluding Arguments To leave out one argument, pass an empty string ( ) to session_set_save_handler().
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Inexpensive Web Hosting services
HTTP and Sessions 133 A Session s Life A PHP 4.0 session is started by calling session_start() or implicitly as soon as you register a session variable with session_register(). On startup of the library, PHP checks whether a valid session ID exists by performing the following actions: 1. If track_vars is set to false, the library checks the global namespace for a session ID. If one is found, the library won t send a cookie with the session ID anymore, but it will also define the SID constant. 2. If track_vars is enabled and no session ID has been found in the global name- space, the $HTTP_COOKIE_VARS array is checked for a session ID. If one is found, no cookie will be sent, and the SID constant won t be defined. 3. If no session ID has been found yet, the $HTTP_GET_VARS and $HTTP_POST_VARS arrays are checked for a session ID. If one is found, the SID constant will be defined. 4. If no session ID has been found yet, the path ($REQUEST_URI) is parsed for a string in the form =. If found, the SID constant will be defined. 5. If the client request specified an external HTTP referrer (from a non-local site) and extern_referer_check (note the single r ) is enabled in the PHP configuration,the session ID is refused and marked as invalid.This introduces some additional security, as it prevents users coming from other PHP sites taking over a session (which is still highly improbable, however, due to the algorithm used for the generation of the session ID). Generally, the SID constant will be defined unless the session library knows for sure that the client supports cookies; in other words, unless the session ID is found in the $HTTP_COOKIE_VARS array. If no session ID has been found with all these checks, or if it has been rejected, it means that a new session should be started and a new session ID is created. If a valid session ID exists, the frozen variables of that session are reactivated and reintroduced to the global namespace. It s as easy to handle session variables as it is to handle GET/POST variables: If you register a variable named foo, $foo is made accessible as a global variable automatically after calling session_start(). It s also added to the global $HTTP_SESSION_VARS array when track_vars is enabled. Because the serialize() function was improved in PHP 4.0, it s also feasible to treat objects (classes) as session variables. Enable track_vars and register_globals You need to have track_vars and register_globals enabled in your PHP configuration to use all functionality of the session-management library.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost PHP Web Hosting services
134 Chapter 4 Web Application Concepts All variables you want to preserve across page requests need to be registered to the session library with the session_register() function. Note that this function takes the name of a variable as argument, not the variable itself.To register the variable $foo, you d use this: session_register( foo ); This code: session_register($foo); would produce something meaningful only if $foo was the name of another variable: $bar = This is a string ; $foo = bar ; session_register($foo); You can use session_unregister() to remove variables from the session library. As with real life, it s not always easy to tell when a session s life ends unless it s a violent death, forced by session_destroy(). If the session is to die of old age, different configurations need to be taken into consideration. If you propagate the session ID via cookies, the default cookie lifetime is 0, meaning that it will be deleted as soon as the user closes the browser.You can influence the cookie s lifetime with the configuration value lifetime. Because the server doesn t know whether the cookie still exists on the client side, PHP has another lifetime variable that determines how long after the last access to this session the data should be destroyed: gc_maxlifetime. But performing such a cleanup of old sessions (called garbage collection) on every page request would cause considerable overhead.Therefore,you can specify with what probability the garbage collection routine should be invoked. If gc_probability is 100, the cleanup will be performed on every request (that is, with a probability of 100%); if it s 1 as by default, old sessions will be removed with a probability of 1% per request. If you don t use cookies but pass the session ID via GET or POST instead, you need to pay special attention to the garbage-collection routines. People might bookmark URLs containing the session ID, so you need to make sure that sessions are cleaned up often if the session data still exists when the user accesses the page with the session ID at a later time, he ll simply resume the previous session instead of starting with a new session, which may not be your intention. On the other hand, don t set the gc_probability too high, especially if you re using file-based session storage. Running a garbage collection in this case involves stat()ing all session files, checking for the last modified time of these sessions.That s a very expensive operation and should not be started too often. Usually a gc_probability of 5 to 10 should be appropriate, especially if you destroy sessions when you re finished with a transaction (for example, when the user checks out of your shop).
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost PHP Web Hosting services