因為數據庫管理不善導致數據丟失,為自己帶來損失的例子不再少數。我們這次就要講到下面代碼顯示了運行 SQL 語句的示例腳本。在本例中,SQL 語句是允許相同攻擊的動態語句。此表單的所有者可能認為表單是安全的,因為他們已經把列名限定為選擇列表。但是,代碼疏忽了關於表單欺騙的最後一個習慣 — 代碼將選項限定為下拉框並不意味著其他人不能夠發布含有所需內容的表單(包括星號 [*])。
- <html>
- <head>
- <title>SQL Injection Example</title>
- </head>
- <body>
- <form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
- method="post">
- <div><input type="text" name="account_number"
- value="<?php echo(isset($_POST['account_number']) ?
- $_POST['account_number'] : ''); ?>" />
- <select name="col">
- <option value="account_number">Account Number</option>
- <option value="name">Name</option>
- <option value="address">Address</option>
- </select>
- <input type="submit" value="Save" name="submit" /></div>
- </form>
- <?php
- if ($_POST['submit'] == 'Save') {
- /* do the form processing */
- $link = mysql_connect('hostname', 'user', 'password') or
- die ('Could not connect' . mysql_error());
- mysql_select_db('test', $link);
- $col = $_POST['col'];
- $select = "SELECT " . $col . " FROM account_data WHERE account_number = "
- . $_POST['account_number'] . ";" ;
- echo '<p>' . $select . '</p>';
- $result = mysql_query($select) or die('<p>' . mysql_error() . '</p>');
- echo '<table>';
- while ($row = mysql_fetch_assoc($result)) {
- echo '<tr>';
- echo '<td>' . $row[$col] . '</td>';
- echo '</tr>';
- }
- echo '</table>';
- mysql_close($link);
- }
- ?>
- </body>
- </html>
因此,要形成PHP保護數據庫的習慣,請盡可能避免使用動態 SQL 代碼。如果無法避免動態 SQL 代碼,請不要對列直接使用輸入。下面則顯示了除使用靜態列外,還可以向帳戶編號字段添加簡單驗證例程以確保輸入值不是非數字值。
- <html>
- <head>
- <title>SQL Injection Example</title>
- </head>
- <body>
- <form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
- method="post">
- <div><input type="text" name="account_number"
- value="<?php echo(isset($_POST['account_number']) ?
- $_POST['account_number'] : ''); ?>" /> <input type="submit"
- value="Save" name="submit" /></div>
- </form>
- <?php
- function isValidAccountNumber($number)
- {
- return is_numeric($number);
- }
- if ($_POST['submit'] == 'Save') {
- /* Remember habit #1--validate your data! */
- if (isset($_POST['account_number']) &
- isValidAccountNumber($_POST['account_number'])) {
- /* do the form processing */
- $link = mysql_connect('hostname', 'user', 'password') or
- die ('Could not connect' . mysql_error());
- mysql_select_db('test', $link);
- $select = sprintf("SELECT account_number, name, address " .
- " FROM account_data WHERE account_number = %s;",
- mysql_real_escape_string($_POST['account_number']));
- echo '<p>' . $select . '</p>';
- $result = mysql_query($select) or die('<p>' . mysql_error() . '</p>');
- echo '<table>';
- while ($row = mysql_fetch_assoc($result)) {
- echo '<tr>';
- echo '<td>' . $row['account_number'] . '</td>';
- echo '<td>' . $row['name'] . '</td>';
- echo '<td>' . $row['address'] . '</td>';
- echo '</tr>';
- }
- echo '</table>';
- mysql_close($link);
- } else {
- echo "<span style="font-color:red">" .
- "Please supply a valid account number!</span>";
- }
- }
- ?>
- </body>
- </html>
在這次PHP保護數據庫的例子中還展示了 mysql_real_escape_string() 函數的用法。此函數將正確地過濾您的輸入,因此它不包括無效字符。如果您一直依賴於 magic_quotes_gpc,那麼需要注意它已被棄用並且將在 PHP V6 中刪除。從現在開始應避免使用它並在此情況下編寫安全的 PHP 應用程序。此外,如果使用的是 ISP,則有可能您的 ISP 沒有啟用 magic_quotes_gpc。
最後,在改進的PHP保護數據庫示例中,您可以看到該 SQL 語句和輸出沒有包括動態列選項。使用這種方法,如果把列添加到稍後含有不同信息的表中,則可以輸出這些列。如果要使用框架以與數據庫結合使用,則您的框架可能已經為您執行了 SQL 驗證。確保查閱文檔以保證框架的安全性;如果仍然不確定,請進行驗證以確保穩妥。即使使用框架進行數據庫交互,仍然需要執行其他驗證。