【网络安全】四、中级篇:SQL注入详解

SQL 注入(SQL Injection)是 Web 安全领域最常见且危害极大的攻击方式之一。它通过将恶意 SQL 代码插入到用户输入中,欺骗数据库执行非预期操作,可能导致数据泄露、权限提升甚至服务器被控。

一、SQL 注入的攻击手段

1. 万能密码注入

1.1 原理

利用 SQL 逻辑漏洞,通过构造恒真条件绕过登录验证。当后台将用户输入直接拼接到 SQL 查询时,攻击者可注入' OR '1'='1' -- 等语句使查询条件永远为真。

1.2 示例场景

假设有登录验证代码:

php 复制代码
// 漏洞代码
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";

攻击方式:在用户名输入框填入 ' OR '1'='1' -- ,密码任意输入。拼接后的 SQL 变为:

sql 复制代码
SELECT * FROM users WHERE username='' OR '1'='1' -- ' AND password='任意值'

-- 注释掉后续语句,'1'='1使条件恒真,直接绕过登录。

2. 联合查询注入(UNION Injection)

2.1 原理

利用UNION操作符拼接额外查询,在页面返回结果中获取数据库信息。要求前后查询的列数一致,常用于获取数据库版本、表名、字段名等敏感信息。

2.2 示例场景

假设有商品查询代码:

php 复制代码
// 漏洞代码
\$id = $\_GET\['id'];
\$sql = "SELECT name, price FROM products WHERE id=\$id";
\$result = mysqli\_query(\$conn, \$sql);

攻击步骤:

  1. 测试列数:?id=1 UNION SELECT 1,2--+(若页面显示 2,则说明存在 2 列)
  2. 获取数据库版本:?id=1 UNION SELECT 1,version()--+
  3. 获取表名:?id=1 UNION SELECT 1,table_name FROM information_schema.tables--+
  4. 获取字段名:?id=1 UNION SELECT 1,column_name FROM information_schema.columns WHERE table_name='users'--+

3. 布尔盲注(Boolean-based Blind Injection)

3.1 原理

当注入不返回明确结果时,通过构造条件判断语句(如AND 1=1/AND 1=2),根据页面返回的真假状态(如 "存在" 或 "不存在")逐步猜解数据。

3.2 示例场景

查询用户是否存在的代码:

php 复制代码
// 漏洞代码
\$id = $\_GET\['id'];
\$sql = "SELECT \* FROM users WHERE id=\$id";
\$result = mysqli\_query(\$conn, \$sql);
if(mysqli\_num\_rows(\$result) > 0) {
    echo "用户存在";
} else {
    echo "用户不存在";
}

攻击步骤:

  1. 判断数据库长度:?id=1 AND length(database())>5--+(根据返回判断长度)
  2. 猜解数据库名:?id=1 AND substr(database(),1,1)='t'--+(逐个字符验证)

4. 时间盲注(Time-based Blind Injection)

4.1 原理

当页面无任何状态差异时,利用sleep()等函数通过延迟响应判断条件是否成立。若条件为真则延迟,为假则正常响应。

4.2 示例场景

无返回信息的查询接口:

php 复制代码
// 漏洞代码
\$id = $\_GET\['id'];
\$sql = "SELECT \* FROM logs WHERE id=\$id";
mysqli\_query(\$conn, \$sql); // 无结果输出

攻击步骤:

  1. 判断数据库首字母:?id=1 AND IF(substr(database(),1,1)='t',sleep(5),1)--+
  2. 若请求延迟 5 秒,则首字母为 't',以此类推猜解完整信息。

二、SQL 注入的防御方法

1. 输入验证与过滤

对用户输入进行严格校验,限制输入格式(如仅允许数字、字母),过滤或转义特殊字符(如'"=#等)。

php 复制代码
// 示例:验证ID必须为数字
\$id = $\_GET\['id'];
if(!is\_numeric(\$id)) {
    die("无效的ID");
}

2. 参数化查询(预编译语句)

核心防御手段:将 SQL 语句与用户输入分离,使用预编译语句传递参数,使数据库无法将参数解析为 SQL 代码。

PHP MySQLi 示例

php 复制代码
// 安全代码
\$id = $\_GET\['id'];
\$stmt = \$conn->prepare("SELECT name, price FROM products WHERE id=?");
\$stmt->bind\_param("i", \$id); // "i"表示参数为整数
\$stmt->execute();
\$result = \$stmt->get\_result();

PHP PDO 示例

php 复制代码
// 安全代码
\$id = $\_GET\['id'];
\$stmt = \$pdo->prepare("SELECT \* FROM users WHERE username=:username");
\$stmt->execute(\[':username' => \$username]);

3. 使用 ORM 框架

ORM(对象关系映射)框架(如 Laravel 的 Eloquent、ThinkPHP 的模型)内部已实现参数化查询,可避免手动拼接 SQL。

php 复制代码
// ThinkPHP ORM示例(安全)
\$user = User::where('username', \$username)->where('password', \$password)->find();

4. 最小权限原则

数据库账号仅分配必要权限(如查询用账号无删除 / 修改权限),即使被注入也能降低危害。

5. 错误信息处理

禁用生产环境的详细错误提示(如不显示mysqli_error()结果),避免泄露数据库结构。

php 复制代码
// 生产环境配置
ini\_set('display\_errors', 0);
error\_reporting(0);

6. 定期安全审计

使用工具(如 SQLMap)扫描潜在漏洞,定期更新框架和数据库补丁。

三、PHPStudy 实战案例

1. 环境搭建

1.1 安装 PHPStudy

官网下载并安装,启动 Apache 和 MySQL 服务。

1.2 创建数据库

  • 在PHPStudy上新建testdb数据库
sql 复制代码
-- 创建用户表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    password VARCHAR(50) NOT NULL
);
INSERT INTO users VALUES (1, 'admin', '123456');

-- 创建商品表
CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10,2) NOT NULL,
    category VARCHAR(50) NOT NULL
);

-- 插入测试数据
INSERT INTO products VALUES 
(1, '笔记本电脑', 5999.99, '电子产品'),
(2, '机械键盘', 399.99, '电脑配件'),
(3, '无线鼠标', 129.99, '电脑配件');

-- 创建日志表
CREATE TABLE logs (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    action VARCHAR(100) NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 插入日志数据
INSERT INTO logs VALUES 
(1, 1, '用户登录', '2023-10-01 08:30:00'),
(2, 1, '查看商品', '2023-10-01 09:15:00');

1.3 创建php文件

  • login.php
php 复制代码
<?php
session_start();
// 修改此处的连接端口、用户名、密码、数据库名
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
if (!$conn) {
    die("数据库连接失败");
}

$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
$login_msg = '';

// 处理登录请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 漏洞代码:直接拼接SQL(无转义、无参数化)
    $sql = "SELECT username FROM users WHERE username='$username' AND password='$password'";
    $result = mysqli_query($conn, $sql);
    
    if (mysqli_num_rows($result) > 0) {
        $_SESSION['username'] = $username;
        header("Location: login_success.php"); // 登录成功页
        exit;
    } else {
        $login_msg = "<span style='color: red;'>用户名或密码错误</span>";
    }
}

mysqli_close($conn);
?>

<!-- 登录页面HTML -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录(万能密码漏洞)</title>
    <style>
        .container { width: 400px; margin: 80px auto; padding: 30px; border: 1px solid #ddd; border-radius: 5px; }
        .form-group { margin: 15px 0; }
        label { display: inline-block; width: 80px; font-size: 16px; }
        input { padding: 8px; width: 250px; border: 1px solid #ddd; border-radius: 3px; }
        button { width: 100%; padding: 10px; margin-top: 10px; background: #0066cc; color: white; border: none; border-radius: 3px; cursor: pointer; }
        .msg { margin-top: 15px; text-align: center; }
    </style>
</head>
<body>
    <div class="container">
        <h2 style="text-align: center; margin-bottom: 20px;">用户登录(万能密码漏洞)</h2>
        <form method="post">
            <div class="form-group">
                <label>用户名:</label>
                <input type="text" name="username" value="<?php echo htmlspecialchars($username); ?>" required>
            </div>
            <div class="form-group">
                <label>密码:</label>
                <input type="password" name="password" required>
            </div>
            <button type="submit">登录</button>
            <?php if ($login_msg): ?>
                <div class="msg"><?php echo $login_msg; ?></div>
            <?php endif; ?>
        </form>
    </div>
</body>
</html>
  • login_success.php
php 复制代码
<!-- 登录成功页(同目录下新建 login_success.php) -->
<?php
// 新建文件:sql-injection-demo/vuln/login_success.php
session_start();
if (!isset($_SESSION['username'])) {
    header("Location: login.php");
    exit;
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>登录成功</title>
    <style>
        .container { width: 500px; margin: 50px auto; text-align: center; }
        .success { font-size: 24px; color: green; margin: 20px 0; }
        a { color: #0066cc; }
    </style>
</head>
<body>
    <div class="container">
        <h2>登录成功!</h2>
        <div class="success">欢迎您,<?php echo htmlspecialchars($_SESSION['username']); ?>!</div>
        <a href="login.php?logout=1">退出登录</a>
    </div>
</body>
</html>
  • product_query.php
php 复制代码
<?php
// 数据库连接(注意:实际开发中不要硬编码密码,此处为测试用)
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
// 连接错误处理
if (!$conn) {
    die("数据库连接失败:" . mysqli_connect_error());
}

// 获取用户输入的商品ID
$product_id = $_GET['product_id'] ?? '';
$product_info = null;
$error_msg = '';

if (!empty($product_id)) {
    // 漏洞代码:直接拼接SQL(无参数化)
    $sql = "SELECT name, price, category FROM products WHERE id=$product_id";
    $result = mysqli_query($conn, $sql);
    
    if ($result) {
        $product_info = mysqli_fetch_assoc($result);
        if (!$product_info) {
            $error_msg = "未找到ID为 $product_id 的商品";
        }
    } else {
        $error_msg = "查询错误:" . mysqli_error($conn);
    }
}

// 关闭连接
mysqli_close($conn);
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>商品查询(存在注入漏洞)</title>
    <style>
        .container { width: 600px; margin: 50px auto; padding: 20px; border: 1px solid #ddd; }
        .form-group { margin: 15px 0; }
        label { display: inline-block; width: 100px; }
        input { padding: 5px; width: 300px; }
        button { padding: 6px 20px; cursor: pointer; }
        .result { margin-top: 20px; padding: 10px; background: #f5f5f5; }
    </style>
</head>
<body>
    <div class="container">
        <h2>商品查询(存在SQL注入漏洞)</h2>
        <!-- 商品查询表单 -->
        <form method="get">
            <div class="form-group">
                <label>商品ID:</label>
                <input type="text" name="product_id" value="<?php echo htmlspecialchars($product_id); ?>" placeholder="输入商品ID(1-3)">
                <button type="submit">查询</button>
            </div>
        </form>

        <!-- 显示查询结果 -->
        <?php if ($error_msg): ?>
            <div class="result" style="color: red;"><?php echo $error_msg; ?></div>
        <?php elseif ($product_info): ?>
            <div class="result">
                <h3>商品信息</h3>
                <p>名称:<?php echo htmlspecialchars($product_info['name']); ?></p>
                <p>价格:<?php echo htmlspecialchars($product_info['price']); ?> 元</p>
                <p>分类:<?php echo htmlspecialchars($product_info['category']); ?></p>
            </div>
        <?php endif; ?>
    </div>
</body>
</html>
  • usser_check.php
php 复制代码
<?php
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
if (!$conn) {
    die("数据库连接失败");
}

$user_id = $_GET['user_id'] ?? '';
$check_result = '';

if (!empty($user_id)) {
    // 漏洞代码:直接拼接SQL
    $sql = "SELECT id FROM users WHERE id=$user_id";
    $result = mysqli_query($conn, $sql);
    
    // 仅返回"存在"或"不存在",无其他信息(符合布尔盲注场景)
    if (mysqli_num_rows($result) > 0) {
        $check_result = "<span style='color: green;'>用户存在</span>";
    } else {
        $check_result = "<span style='color: red;'>用户不存在</span>";
    }
}

mysqli_close($conn);
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户存在查询(布尔盲注漏洞)</title>
    <style>
        .container { width: 500px; margin: 50px auto; }
        .form-group { margin: 20px 0; }
        input { padding: 5px; width: 200px; }
        .result { margin-top: 20px; font-size: 18px; }
    </style>
</head>
<body>
    <div class="container">
        <h2>用户存在查询(布尔盲注漏洞)</h2>
        <form method="get">
            <div class="form-group">
                <label>用户ID:</label>
                <input type="text" name="user_id" value="<?php echo htmlspecialchars($user_id); ?>" placeholder="输入用户ID">
                <button type="submit">查询</button>
            </div>
        </form>
        <?php if ($check_result): ?>
            <div class="result">查询结果:<?php echo $check_result; ?></div>
        <?php endif; ?>
    </div>
</body>
</html>
  • log_record.php
php 复制代码
<?php
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
if (!$conn) {
    die("数据库连接失败");
}

$log_id = $_GET['log_id'] ?? '';
$page_msg = "日志操作已执行"; // 固定提示,无结果差异(符合时间盲注场景)

if (!empty($log_id)) {
    // 漏洞代码:直接拼接SQL,无结果输出
    $sql = "SELECT action FROM logs WHERE id=$log_id";
    mysqli_query($conn, $sql); // 仅执行查询,不返回任何结果
}

mysqli_close($conn);
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>日志记录(时间盲注漏洞)</title>
    <style>
        .container { width: 500px; margin: 50px auto; }
        .form-group { margin: 20px 0; }
        input { padding: 5px; width: 200px; }
        .msg { margin-top: 20px; font-size: 16px; color: #333; }
    </style>
</head>
<body>
    <div class="container">
        <h2>日志记录(时间盲注漏洞)</h2>
        <form method="get">
            <div class="form-group">
                <label>日志ID:</label>
                <input type="text" name="log_id" value="<?php echo htmlspecialchars($log_id); ?>" placeholder="输入日志ID">
                <button type="submit">执行日志操作</button>
            </div>
        </form>
        <div class="msg"><?php echo $page_msg; ?></div>
    </div>
</body>
</html>
  • 文件目录结构如下
bash 复制代码
PHPStudy/WWW/​
└── sql-injection-demo/​
    ├── vuln/                # 漏洞文件目录​
    │   ├── product_query.php  # 联合查询注入(商品)​
    │   ├── user_check.php     # 布尔盲注(用户查询)​
    │   ├── log_record.php     # 时间盲注(日志)​
    │   ├── login.php          # 万能密码注入(登录)​
    │   └── login_success.php  # 登录成功页(漏洞版)​
    └── safe/                # 安全文件目录​
        ├── product_query.php  # 联合查询修复版​
        ├── user_check.php     # 布尔盲注修复版​
        ├── log_record.php     # 时间盲注修复版​
        ├── login.php          # 万能密码修复版​
        └── login_success.php  # 登录成功页(安全版)

2. 万能密码注入

2.1 攻击演示

  • 访问 http://localhost/vuln/login.php​
  • 输入万能密码:
    用户名:' OR '1'='1' -- -- 注释掉后续密码判断)
    密码:任意字符(如123)
  • 点击登录,直接跳转到login_success.php,绕过验证

2.2 防御

修改login.phplogin_success.phpmysqli_prepare会将不完整的SQL语句先编译,用户输入只能作为参数。

php 复制代码
<?php
// safe/login.php
session_start();
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
if (!$conn) {
    die("数据库连接失败");
}

$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
$login_msg = '';

// 退出登录逻辑
if (isset($_GET['logout'])) {
    session_destroy();
    header("Location: login.php");
    exit;
}

// 处理登录请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 安全代码:参数化查询
    $sql = "SELECT username, password FROM users WHERE username=?";
    $stmt = mysqli_prepare($conn, $sql);
    mysqli_stmt_bind_param($stmt, "s", $username); // "s"表示字符串类型
    mysqli_stmt_execute($stmt);
    $result = mysqli_stmt_get_result($stmt);
    $user = mysqli_fetch_assoc($result);
    
    // 验证密码(注意:实际开发中密码需加密存储,此处为演示用明文)
    if ($user && $user['password'] === $password) {
        $_SESSION['username'] = $user['username'];
        header("Location: login_success.php");
        exit;
    } else {
        $login_msg = "<span style='color: red;'>用户名或密码错误</span>";
    }
    mysqli_stmt_close($stmt);
}

mysqli_close($conn);
?>

<!-- safe/login_success.php -->
<?php
session_start();
if (!isset($_SESSION['username'])) {
    header("Location: login.php");
    exit;
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>登录成功(安全版)</title>
    <style>
        .container { width: 500px; margin: 50px auto; text-align: center; }
        .success { font-size: 24px; color: green; margin: 20px 0; }
        .tip { color: #666; margin: 10px 0; }
        a { color: #0066cc; }
    </style>
</head>
<body>
    <div class="container">
        <h2>登录成功(安全版)</h2>
        <div class="success">欢迎您,<?php echo htmlspecialchars($_SESSION['username']); ?>!</div>
        <div class="tip">提示:实际开发中需用md5/sha256等加密存储密码</div>
        <a href="login.php?logout=1">退出登录</a>
    </div>
</body>
</html>

<!-- 登录页面HTML(safe/login.php 后续代码) -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录(安全版)</title>
    <style>
        .container { width: 400px; margin: 80px auto; padding: 30px; border: 1px solid #ddd; border-radius: 5px; }
        .form-group { margin: 15px 0; }
        label { display: inline-block; width: 80px; font-size: 16px; }
        input { padding: 8px; width: 250px; border: 1px solid #ddd; border-radius: 3px; }
        button { width: 100%; padding: 10px; margin-top: 10px; background: #0066cc; color: white; border: none; border-radius: 3px; cursor: pointer; }
        .msg { margin-top: 15px; text-align: center; }
        .safe-tip { font-size: 12px; color: #666; margin-top: 20px; text-align: center; }
    </style>
</head>
<body>
    <div class="container">
        <h2 style="text-align: center; margin-bottom: 20px;">用户登录(安全版:参数化查询)</h2>
        <form method="post">
            <div class="form-group">
                <label>用户名:</label>
                <input type="text" name="username" value="<?php echo htmlspecialchars($username); ?>" required>
            </div>
            <div class="form-group">
                <label>密码:</label>
                <input type="password" name="password" required>
            </div>
            <button type="submit">登录</button>
            <?php if ($login_msg): ?>
                <div class="msg"><?php echo $login_msg; ?></div>
            <?php endif; ?>
        </form>
        <div class="safe-tip">万能密码注入已被拦截(参数化查询保护)</div>
    </div>
</body>
</html>

3. 联合查询注入(商品查询)

3.1 攻击演示

  • 访问漏洞页面:http://localhost/vuln/product_query.php
  • 正常查询:输入product_id=1,点击查询,显示笔记本电脑信息
  • 判断列数:输入'' UNION SELECT 1,2,3--+(返回正常,说明表有 3 列),--+的核心作用是注释掉注入点后续的 SQL 代码
  • 获取数据库版本:输入'' UNION SELECT 1,version(),database()--+(显示 MySQL 版本和testdb数据库)
  • 读取用户表数据:输入'' UNION SELECT username,password,'用户表' FROM users--+(直接获取admin的账号密码)

3.2 防御

修改product_query.php文件

php 复制代码
<?php
// 数据库连接
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
if (!$conn) {
    die("数据库连接失败:" . mysqli_connect_error());
}

$product_id = $_GET['product_id'] ?? '';
$product_info = null;
$error_msg = '';

if (!empty($product_id)) {
    // 安全代码:使用参数化查询(预编译语句)
    $sql = "SELECT name, price, category FROM products WHERE id=?";
    // 准备预编译语句
    $stmt = mysqli_prepare($conn, $sql);
    // 绑定参数("i"表示参数为整数类型)
    mysqli_stmt_bind_param($stmt, "i", $product_id);
    // 执行查询
    mysqli_stmt_execute($stmt);
    // 获取结果集
    $result = mysqli_stmt_get_result($stmt);
    
    if ($result) {
        $product_info = mysqli_fetch_assoc($result);
        if (!$product_info) {
            $error_msg = "未找到ID为 $product_id 的商品";
        }
    } else {
        $error_msg = "查询错误:" . mysqli_stmt_error($stmt);
    }
    // 关闭预编译语句
    mysqli_stmt_close($stmt);
}

mysqli_close($conn);
?>

<!-- HTML部分与漏洞文件完全一致,此处省略(直接复制漏洞文件的HTML代码即可) -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>商品查询(安全版)</title>
    <style>
        .container { width: 600px; margin: 50px auto; padding: 20px; border: 1px solid #ddd; }
        .form-group { margin: 15px 0; }
        label { display: inline-block; width: 100px; }
        input { padding: 5px; width: 300px; }
        button { padding: 6px 20px; cursor: pointer; }
        .result { margin-top: 20px; padding: 10px; background: #f5f5f5; }
    </style>
</head>
<body>
    <div class="container">
        <h2>商品查询(安全版:参数化查询)</h2>
        <form method="get">
            <div class="form-group">
                <label>商品ID:</label>
                <input type="text" name="product_id" value="<?php echo htmlspecialchars($product_id); ?>" placeholder="输入商品ID(1-3)">
                <button type="submit">查询</button>
            </div>
        </form>

        <?php if ($error_msg): ?>
            <div class="result" style="color: red;"><?php echo $error_msg; ?></div>
        <?php elseif ($product_info): ?>
            <div class="result">
                <h3>商品信息</h3>
                <p>名称:<?php echo htmlspecialchars($product_info['name']); ?></p>
                <p>价格:<?php echo htmlspecialchars($product_info['price']); ?> 元</p>
                <p>分类:<?php echo htmlspecialchars($product_info['category']); ?></p>
            </div>
        <?php endif; ?>
    </div>
</body>
</html>

4. 布尔盲注(用户存在查询)

4.1 攻击演示

  • 访问 http://localhost/vuln/user_check.php
  • 猜解数据库名长度:输入user_id=1 AND length(database())=6--+(返回 "用户存在",说明数据库名长度为 6,即testdb)
  • 猜解数据库首字母:输入user_id=1 AND substr(database(),1,1)='t'--+(返回 "存在",首字母为t),substr的第二个参数表示截取的起始位置,第三个参数表示要截取的字符长度
  • 逐步猜解:通过修改substr的第三个参数(1→2→3...),最终得到完整数据库名testdb

4.2 防御

修改user_check.php,同样是通过参数化注入。

php 复制代码
<?php
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
if (!$conn) {
    die("数据库连接失败");
}

$user_id = $_GET['user_id'] ?? '';
$check_result = '';

if (!empty($user_id)) {
    // 第一步:输入验证(仅允许数字)
    if (!is_numeric($user_id)) {
        $check_result = "<span style='color: orange;'>无效ID:仅允许输入数字</span>";
    } else {
        // 第二步:参数化查询
        $sql = "SELECT id FROM users WHERE id=?";
        $stmt = mysqli_prepare($conn, $sql);
        mysqli_stmt_bind_param($stmt, "i", $user_id);
        mysqli_stmt_execute($stmt);
        $result = mysqli_stmt_get_result($stmt);
        
        if (mysqli_num_rows($result) > 0) {
            $check_result = "<span style='color: green;'>用户存在</span>";
        } else {
            $check_result = "<span style='color: red;'>用户不存在</span>";
        }
        mysqli_stmt_close($stmt);
    }
}

mysqli_close($conn);
?>

<!-- HTML部分与漏洞文件一致,此处省略(复制漏洞文件的HTML代码) -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户存在查询(安全版)</title>
    <style>
        .container { width: 500px; margin: 50px auto; }
        .form-group { margin: 20px 0; }
        input { padding: 5px; width: 200px; }
        .result { margin-top: 20px; font-size: 18px; }
    </style>
</head>
<body>
    <div class="container">
        <h2>用户存在查询(安全版:输入验证+参数化)</h2>
        <form method="get">
            <div class="form-group">
                <label>用户ID:</label>
                <input type="text" name="user_id" value="<?php echo htmlspecialchars($user_id); ?>" placeholder="输入用户ID">
                <button type="submit">查询</button>
            </div>
        </form>
        <?php if ($check_result): ?>
            <div class="result">查询结果:<?php echo $check_result; ?></div>
        <?php endif; ?>
    </div>
</body>
</html>

5. 时间盲注(日志记录)

5.1 攻击演示

  • 访问 http://localhost/vuln/log_record.php
  • 验证注入点:输入log_id=1 AND sleep(5)--+(页面延迟 5 秒后显示提示,说明存在时间盲注)
  • 猜解用户表是否存在:输入log_id=1 AND IF(exists(select * from users),sleep(5),1)--+(延迟 5 秒,说明users表存在)

5.2 防御

修改log_record.php文件,核心同样是参数化查询。

php 复制代码
<?php
$conn = mysqli_connect('localhost', 'root', 'root', 'testdb');
if (!$conn) {
    die("数据库连接失败");
}

$log_id = $_GET['log_id'] ?? '';
$page_msg = "日志操作已执行";

if (!empty($log_id)) {
    // 安全代码:参数化查询
    $sql = "SELECT action FROM logs WHERE id=?";
    $stmt = mysqli_prepare($conn, $sql);
    mysqli_stmt_bind_param($stmt, "i", $log_id);
    mysqli_stmt_execute($stmt); // 执行查询,无结果输出
    mysqli_stmt_close($stmt);
}

mysqli_close($conn);
?>

<!-- HTML部分与漏洞文件一致,此处省略 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>日志记录(安全版)</title>
    <style>
        .container { width: 500px; margin: 50px auto; }
        .form-group { margin: 20px 0; }
        input { padding: 5px; width: 200px; }
        .msg { margin-top: 20px; font-size: 16px; color: #333; }
    </style>
</head>
<body>
    <div class="container">
        <h2>日志记录(安全版:参数化查询)</h2>
        <form method="get">
            <div class="form-group">
                <label>日志ID:</label>
                <input type="text" name="log_id" value="<?php echo htmlspecialchars($log_id); ?>" placeholder="输入日志ID">
                <button type="submit">执行日志操作</button>
            </div>
        </form>
        <div class="msg"><?php echo $page_msg; ?></div>
    </div>
</body>
</html>
相关推荐
FIN666812 小时前
新天力:食品容器安全与创新的领航者
科技·安全·产品运营·创业创新·制造
alex10012 小时前
BeaverTails数据集:大模型安全对齐的关键资源与实战应用
人工智能·算法·安全
鹿鸣天涯12 小时前
Kali Linux 2025.3 正式发布:更贴近前沿的安全平台
linux·运维·安全
!chen13 小时前
Harbor磁盘空间清理指南:如何安全清理半年前的镜像
安全
alex10013 小时前
Context Compliance Attack:大模型安全的新兴威胁与防御策略
网络·安全·web安全
xiejava101816 小时前
开源安全管理平台wazuh-安装与配置
安全·开源
五颜六色的池17 小时前
my sql 常用函数及语句的执行顺序
数据库·sql
粟悟饭&龟波功18 小时前
【网络安全】三、入门篇:Web安全常见漏洞概述
安全·web安全
FIN666818 小时前
新天力科技IPO进行时:技术引领未来,创新驱动发展
科技·安全·搜索引擎·产品运营·创业创新·制造