SQL 注入

在本教程中,你將學習如何修復常見的資料庫漏洞。

什麼是 SQL 注入?

SQL 注入是一種攻擊,其中攻擊者可以通過從瀏覽器到應用程式伺服器的輸入資料(例如 Web 表單輸入)注入或執行惡意 SQL 程式碼。

它可用於公開敏感資訊,如使用者的聯絡電話,電子郵件地址,信用卡資訊等。攻擊者甚至可以使用它來繞過身份驗證過程並訪問整個資料庫。讓我們看看它是如何運作的。

SQL 注入的工作原理

請考慮以下 SQL 語句,該語句是在 Web 應用程式中使用使用者名稱和密碼對使用者進行身份驗證的簡單示例。

SELECT * FROM users WHERE username='username_val' AND password='password_val'; 

這裡,username_valpassword_val 分別代表使用者輸入的使用者名稱和密碼。如果使用者輸入 john 等值作為使用者名稱,輸入 123 作為密碼,則生成的語句將為:

SELECT * FROM users WHERE username='john' AND password='123'; 

但是假設,如果使用者是攻擊者而不是在輸入欄位中輸入有效的使用者名稱和密碼,他輸入的值如下: ' OR 'x'='x

在這種情況下,上面的 SQL 查詢將構造為:

SELECT * FROM users WHERE username= '' OR 'x' = 'x' AND password= '' OR 'x' = 'x' ; 

此語句是有效的 SQL 語句,因為 WHERE 'x'='x' 始終為 true,查詢將返回 users 表中的所有行。你可以看到攻擊者只需要一點點骯髒的技巧就可以輕鬆訪問資料庫的所有敏感資訊。

如果 users 表非常大並且包含數百萬或行,則此單個語句還可能通過使系統資源過載並使你的應用程式對合法使用者不可用而導致拒絕服務攻擊(DoS 攻擊)。

警告: 如果指令碼生成 DELETEUPDATE 查詢,則忽略 SQL 注入漏洞的後果可能更糟。攻擊者可以從表中刪除資料或永久更改其所有行。

防止 SQL 注入

始終驗證使用者輸入並且不做任何假設。永遠不要直接從使用者輸入構建 SQL 語句。如果你使用的是 PHP 和 MySQL,則可以使用 mysqli_real_escape_string() 函式建立可在 SQL 語句中使用的合法 SQL 字串。

這是使用 PHP 和 MySQL 進行使用者身份驗證的一個非常基本的示例,演示瞭如何在從使用者那裡獲取輸入時阻止 SQL 注入。

<?php
// Starting session
session_start();
 
/* Attempt MySQL server connection. Assuming you are running MySQL
server with default setting (user 'root' with no password) */
$link = mysqli_connect("localhost", "root", "", "demo");
 
// Check connection
if($link === false){
    die("ERROR: Could not connect to database.");
}
 
// Escape user inputs for security
$username_val = mysqli_real_escape_string($link, $_POST['username']);
$password_val = mysqli_real_escape_string($link, $_POST['password']);
 
if(isset($username_val, $password_val)){
    // Attempt select query execution
    $sql = "SELECT * FROM users WHERE username='" . $username_val . "' AND password='" . $password_val . "'";
    if($result = mysqli_query($link, $sql)){
        if(mysqli_num_rows($result) == 1){
            // User is authenticated do your stuff here
            $row = mysqli_fetch_array($result);
            /* Holding values in session variable so that it can be
            accessed later within the same session reference */
            $_SESSION['user_id'] = $row['user_id'];
            $_SESSION['first_name'] = $row['first_name'];
            header('Location: welcome.php');
        } else{
            echo "ERROR: Invalid username or password.";
        }
    } else{
        echo "ERROR: Something went wrong. Please try again.";
    }
}
 
// Close connection
mysqli_close($link);
?>

提示: 測試應用程式接收的資料的大小和型別或內容,並實施適當的限制以防止系統資源利用。