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);
?>

提示: 测试应用程序接收的数据的大小和类型或内容,并实施适当的限制以防止系统资源利用。