12.4. Presenting a Form to Add and Process in One FileWe're building a form that allows a web user to add a title to the books table. Example 12-6 is a slightly longer example, because we display and process the form in one file, but it should look familiar to you since we're simply combining several steps that we've done separately before. Example 12-6. Using input from a form to add a title
Example 12-6 begins by displaying a form like Figure 12-6, using the code in the body of the file if the $title and $pages values do not have both values set. Figure 12-6. This is how the form looks with some sample data in the fieldOnce the user enters values into both fields and clicks the Go! button, the same script handles the form submission processing. Since values exist for the two fields, the insert_db function is called with those values. The values are placed into the query string enclosed by single quotes (' '): $query = "INSERT INTO 'books' VALUES (NULL,'$title','$pages')"; This query is then executed like any other query. Finally, the function queries the books table and displays the resuss in an HTML table. Figure 12-7 shows what happens after clicking the Go! button with the sample data above. Figure 12-7. The results page shows the new entryYou must take several precautions when working with strings submitted from a form that will be processed by the database. 12.4.1. SQL InjectionSpecifically, you need to be on guard for a tactic called SQL injection. SQL injection is when a malicious user enters another SQL query into a field such as 1,1);drop table users;. When this query is added to a query like this: $query = "INSERT INTO 'books' VALUES (NULL,$title,$pages)"; here's what could happen: $query = "INSERT INTO 'books' VALUES (NULL,1,1);drop table users; ,$pages)"; PHP and MySQL work together to thwart this kind of attack. What happens is the MySQL query command allows only one statement per query. So attempting to start a new query after the first one has already been started generates an error. Additionally, PHP uses a system by default called magic quotes with user input. Magic quotes automatically escape any special characters with a backslash (\), including single and double quotes. Example 12-7 shows how to test whether magic quotes are enabled on your installation of PHP. Example 12-7. Checking for magic quotes
The script should return: Magic quotes are enabled. Because the PEAR DB abstraction layer is being used, MySQL-specific escaping should not be used simultaneously. Use the PEAR escapeSimple($string) function to do your escaping with PEAR code. If magic quotes are off, you can use the add_slashes function to accomplish the same thing with your input. Nonetheless, you should look out for these errors, since other databases may allow more than one statement per query. Be skeptical of user input, or you could end up with a compromised database. 12.4.2. Cross-Site Scripting AttacksOne last gotcha to look out for when using data from user input is the risk of cross-site scripting attacks. These attacks work slightly differently than SQL injection. They don't compromise the data on your server, but instead can lead to a user's browser giving out sensitive data to a third party because the browser thinks the command came from your trusted site. To guard against these attacks, you should pass any strings that came from a user through the htmlentities function. It takes the format: htmlentities(string_to_clean) For example: print "The title of the book is: " . htmlentities($_POST['title']); Here's an example of what htmlentities does to the string: <?php $sample = "A sample is <i>italics</i>"; echo htmlentities($sample); ?> When executed, this returns: A sample is <i>italics</i> Essentially, you're guarding against the same problem as SQL injection, but the code that's vulnerable is the HTML. The special function HTML characters such as less than (<) and greater than (>) are escaped, preventing hostile HTML code from working when displayed from your site. Here's a script to display the title table with the htmlentities functionality added: <?php require_once('db_login.php'); require_once('DB.php'); $connection = DB::connect("mysql://$db_username:$db_password@$db_host/$db_database"); if (DB::isError($connection)){ die ("Could not connect to the database: <br />". DB::errorMessage($connection)); } // Dislplay the table $query = "SELECT * FROM `books`"; $result = $connection->query($query); if (DB::isError($result)){ die("Could not query the database: <br />".$query." ".DB::errorMessage($result)); } echo '<table border="1">'; echo "<tr><th>Title</th><th>Pages</th></tr>"; while ($result_row = $result->fetchRow(DB_FETCHMODE_ASSOC)) { echo "<tr><td>"; echo htmlentities($result_row["title"]) . '</td><td>'; echo htmlentities($result_row["pages"]) . '</td></tr>'; } echo "</table>"; $connection->disconnect(); ?> Figure 12-8 shows that htmlentities didn't change the look of your table. Figure 12-8. No change is made to the look of your tableYou can be assured that you've prevented any malicious HTML that may have been entered by a user from confusing another user's browser. |