1. 程式人生 > >防禦SQL注入方法(2)-過濾特殊字元

防禦SQL注入方法(2)-過濾特殊字元

防禦SQL注入的一個重要方法是對使用者輸入進行過濾,PHP中mysqli_real_escape_string(connection,escapestring)和mysqli_escape_string(connection,escapestring)函式就是對使用者輸入進行轉義的,兩者功能一樣。mysql_real_escape_string()與mysql_escape_string()的轉義方式在PHP5.3以上的版本中已經棄用。以DVWA中SQL注入原始碼為例,瞭解過濾函式的用法。
(1)沒有任何過濾

<?php

if( isset( $_REQUEST[ 'Submit'
] ) ) { // Get input $id = $_REQUEST[ 'id' ]; // Check database $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS
["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; } mysqli_close($GLOBALS["___mysqli_ston"]); } ?>

idSQLquery = “SELECT first_name, last_name FROM users WHERE user_id = ‘$id’;”,存在SQL注入的風險。
(2)使用mysqli_real_escape_string(connection,escapestring)函式過濾

<?php

if( isset( $_POST[ 'Submit' ] ) ) {
    // Get input
    $id = $_POST[ 'id' ];

    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
    $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );

    // Get results
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // Display values
        $first = $row["first_name"];
        $last  = $row["last_name"];

        // Feedback for end user
        $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }

}

// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query  = "SELECT COUNT(*) FROM users;";
$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
$number_of_rows = mysqli_fetch_row( $result )[0];

mysqli_close($GLOBALS["___mysqli_ston"]);
?>

使用mysqli_real_escape_string(connection,escapestring)函式對輸入$id進行過濾,可以防止特殊字元造成的SQL命令拼接。
但是現在使用mysqli_real_escape_string()已經不是最好的方法了,提倡使用PDO(PHP資料物件),需要PHP5以上版本的面向物件作為支援。它是在底層實現的資料庫操作介面,可以實現高階的資料庫操作,比如呼叫儲存過程,因此安全性更高。下面同樣借用DVWA原始碼進行說明。
(3)安全性更高的PDO

<?php

if( isset( $_GET[ 'Submit' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $id = $_GET[ 'id' ];

    // Was a number entered?
    if(is_numeric( $id )) {
        // Check the database
        $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
        $data->bindParam( ':id', $id, PDO::PARAM_INT );
        $data->execute();
        $row = $data->fetch();

        // Make sure only 1 result is returned
        if( $data->rowCount() == 1 ) {
            // Get values
            $first = $row[ 'first_name' ];
            $last  = $row[ 'last_name' ];

            // Feedback for end user
            $html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
        }
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>

此例來自DVWA安全性等級為impossible的原始碼,例子中使用PDO物件的同時還使用了預編譯函式prepare()繫結變數,更加確保了SQL語句的正確執行。