【网络安全】四、中级篇:SQL注入详解
SQL 注入(SQL Injection)是 Web 安全领域最常见且危害极大的攻击方式之一。它通过将恶意 SQL 代码插入到用户输入中,欺骗数据库执行非预期操作,可能导致数据泄露、权限提升甚至服务器被控。
一、SQL 注入的攻击手段
1. 万能密码注入
1.1 原理
利用 SQL 逻辑漏洞,通过构造恒真条件绕过登录验证。当后台将用户输入直接拼接到 SQL 查询时,攻击者可注入' OR '1'='1' --
等语句使查询条件永远为真。
1.2 示例场景
假设有登录验证代码:
// 漏洞代码
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
攻击方式:在用户名输入框填入 ' OR '1'='1' --
,密码任意输入。拼接后的 SQL 变为:
SELECT * FROM users WHERE username='' OR '1'='1' -- ' AND password='任意值'
--
注释掉后续语句,'1'='1
使条件恒真,直接绕过登录。
2. 联合查询注入(UNION Injection)
2.1 原理
利用UNION
操作符拼接额外查询,在页面返回结果中获取数据库信息。要求前后查询的列数一致,常用于获取数据库版本、表名、字段名等敏感信息。
2.2 示例场景
假设有商品查询代码:
// 漏洞代码
\$id = $\_GET\['id'];
\$sql = "SELECT name, price FROM products WHERE id=\$id";
\$result = mysqli\_query(\$conn, \$sql);
攻击步骤:
- 测试列数:
?id=1 UNION SELECT 1,2--+
(若页面显示 2,则说明存在 2 列) - 获取数据库版本:
?id=1 UNION SELECT 1,version()--+
- 获取表名:
?id=1 UNION SELECT 1,table_name FROM information_schema.tables--+
- 获取字段名:
?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 示例场景
查询用户是否存在的代码:
// 漏洞代码
\$id = $\_GET\['id'];
\$sql = "SELECT \* FROM users WHERE id=\$id";
\$result = mysqli\_query(\$conn, \$sql);
if(mysqli\_num\_rows(\$result) > 0) {
  echo "用户存在";
} else {
  echo "用户不存在";
}
攻击步骤:
- 判断数据库长度:
?id=1 AND length(database())>5--+
(根据返回判断长度) - 猜解数据库名:
?id=1 AND substr(database(),1,1)='t'--+
(逐个字符验证)
4. 时间盲注(Time-based Blind Injection)
4.1 原理
当页面无任何状态差异时,利用sleep()
等函数通过延迟响应判断条件是否成立。若条件为真则延迟,为假则正常响应。
4.2 示例场景
无返回信息的查询接口:
// 漏洞代码
\$id = $\_GET\['id'];
\$sql = "SELECT \* FROM logs WHERE id=\$id";
mysqli\_query(\$conn, \$sql); // 无结果输出
攻击步骤:
- 判断数据库首字母:
?id=1 AND IF(substr(database(),1,1)='t',sleep(5),1)--+
- 若请求延迟 5 秒,则首字母为 ‘t’,以此类推猜解完整信息。
二、SQL 注入的防御方法
1. 输入验证与过滤
对用户输入进行严格校验,限制输入格式(如仅允许数字、字母),过滤或转义特殊字符(如'
、"
、=
、#
等)。
// 示例:验证ID必须为数字
\$id = $\_GET\['id'];
if(!is\_numeric(\$id)) {
  die("无效的ID");
}
2. 参数化查询(预编译语句)
核心防御手段:将 SQL 语句与用户输入分离,使用预编译语句传递参数,使数据库无法将参数解析为 SQL 代码。
PHP MySQLi 示例:
// 安全代码
\$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 示例:
// 安全代码
\$id = $\_GET\['id'];
\$stmt = \$pdo->prepare("SELECT \* FROM users WHERE username=:username");
\$stmt->execute(\[':username' => \$username]);
3. 使用 ORM 框架
ORM(对象关系映射)框架(如 Laravel 的 Eloquent、ThinkPHP 的模型)内部已实现参数化查询,可避免手动拼接 SQL。
// ThinkPHP ORM示例(安全)
\$user = User::where('username', \$username)->where('password', \$password)->find();
4. 最小权限原则
数据库账号仅分配必要权限(如查询用账号无删除 / 修改权限),即使被注入也能降低危害。
5. 错误信息处理
禁用生产环境的详细错误提示(如不显示mysqli_error()
结果),避免泄露数据库结构。
// 生产环境配置
ini\_set('display\_errors', 0);
error\_reporting(0);
6. 定期安全审计
使用工具(如 SQLMap)扫描潜在漏洞,定期更新框架和数据库补丁。
三、PHPStudy 实战案例
1. 环境搭建
1.1 安装 PHPStudy
官网下载并安装,启动 Apache 和 MySQL 服务。
1.2 创建数据库
- 在PHPStudy上新建
testdb
数据库
-- 创建用户表
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
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
<!-- 登录成功页(同目录下新建 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
// 数据库连接(注意:实际开发中不要硬编码密码,此处为测试用)
$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
$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
$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>
- 文件目录结构如下
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.php
和login_success.php
,mysqli_prepare
会将不完整的SQL语句先编译,用户输入只能作为参数。
<?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
// 数据库连接
$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
$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
$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>