以下是一个使用 PHP 和 MySQL 实现的安全用户认证和权限管理的示例,包括修复修改 cookie 中 is_admin 值的漏洞。
---
完整实现流程
1. 数据库设计
一个简单的用户表(users)和会话表(sessions):
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
is_admin BOOLEAN NOT NULL DEFAULT 0
);
CREATE TABLE sessions (
id CHAR(64) PRIMARY KEY, -- 会话 ID
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
users 表存储用户信息,包括是否管理员 (is_admin)。
sessions 表存储会话信息,使用随机生成的 session_id 关联用户。
---
2. 用户登录逻辑
<?php
session_start();
include 'db.php'; // 数据库连接文件
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
// 查询用户
$stmt = $db->prepare("SELECT id, password_hash, is_admin FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if ($user && password_verify($password, $user['password_hash'])) {
// 登录成功,生成会话
$session_id = bin2hex(random_bytes(32)); // 生成随机会话 ID
$stmt = $db->prepare("INSERT INTO sessions (id, user_id) VALUES (?, ?)");
$stmt->bind_param("si", $session_id, $user['id']);
$stmt->execute();
// 设置安全的 cookie
setcookie('session_id', $session_id, [
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
echo "Login successful!";
} else {
echo "Invalid username or password.";
}
}
?>
---
3. 后台访问逻辑
<?php
session_start();
include 'db.php'; // 数据库连接文件
if (!isset($_COOKIE['session_id'])) {
die("Unauthorized access.");
}
$session_id = $_COOKIE['session_id'];
// 验证会话并获取用户信息
$stmt = $db->prepare("
SELECT u.id, u.username, u.is_admin
FROM sessions s
JOIN users u ON s.user_id = u.id
WHERE s.id = ?
");
$stmt->bind_param("s", $session_id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if (!$user) {
die("Invalid session.");
}
if (!$user['is_admin']) {
die("Forbidden: You don't have permission to access this page.");
}
// 如果通过验证,显示后台内容
echo "Welcome to the admin dashboard, " . htmlspecialchars($user['username']) . "!";
?>
---
4. 登出逻辑
<?php
session_start();
include 'db.php'; // 数据库连接文件
if (isset($_COOKIE['session_id'])) {
$session_id = $_COOKIE['session_id'];
// 删除会话记录
$stmt = $db->prepare("DELETE FROM sessions WHERE id = ?");
$stmt->bind_param("s", $session_id);
$stmt->execute();
// 清除 cookie
setcookie('session_id', '', time() - 3600, '/');
}
echo "Logged out successfully.";
?>
---
关键点说明
1. 会话管理:
会话 ID (session_id) 是随机生成的,攻击者无法轻易猜测。
会话信息保存在服务端数据库中,不依赖客户端数据。
2. 权限校验:
后端根据 session_id 查询用户权限 (is_admin),而非直接信任客户端传递的 is_admin 值。
3. 密码存储:
使用 password_hash() 和 password_verify() 安全地处理密码。
4. Cookie 安全性:
设置 HttpOnly 和 Secure 标志,防止 XSS 和中间人攻击。
SameSite=Strict 防止 CSRF 攻击。
5. 输入验证:
使用 prepared statements 防止 SQL 注入。
避免直接输出用户输入内容,使用 htmlspecialchars() 进行输出过滤。
---
运行逻辑示例
1. 用户登录:
用户提供用户名和密码。
系统验证密码并生成安全的 session_id。
session_id 存储在服务端数据库,并通过 cookie 传递给客户端。
2. 后台访问:
用户发起请求时,系统验证 session_id 的合法性。
根据数据库中用户的 is_admin 字段判断权限。
3. 权限不足:
如果用户 is_admin 为 0,拒绝访问后台。
---
总结
通过以上方式修复漏洞,可以确保即使攻击者篡改 cookie,也无法获得管理员权限,因为权限验证完全在后端进行,依赖于服务端的安全存储和验证逻辑。
---
完整实现流程
1. 数据库设计
一个简单的用户表(users)和会话表(sessions):
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
is_admin BOOLEAN NOT NULL DEFAULT 0
);
CREATE TABLE sessions (
id CHAR(64) PRIMARY KEY, -- 会话 ID
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
users 表存储用户信息,包括是否管理员 (is_admin)。
sessions 表存储会话信息,使用随机生成的 session_id 关联用户。
---
2. 用户登录逻辑
<?php
session_start();
include 'db.php'; // 数据库连接文件
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
// 查询用户
$stmt = $db->prepare("SELECT id, password_hash, is_admin FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if ($user && password_verify($password, $user['password_hash'])) {
// 登录成功,生成会话
$session_id = bin2hex(random_bytes(32)); // 生成随机会话 ID
$stmt = $db->prepare("INSERT INTO sessions (id, user_id) VALUES (?, ?)");
$stmt->bind_param("si", $session_id, $user['id']);
$stmt->execute();
// 设置安全的 cookie
setcookie('session_id', $session_id, [
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
echo "Login successful!";
} else {
echo "Invalid username or password.";
}
}
?>
---
3. 后台访问逻辑
<?php
session_start();
include 'db.php'; // 数据库连接文件
if (!isset($_COOKIE['session_id'])) {
die("Unauthorized access.");
}
$session_id = $_COOKIE['session_id'];
// 验证会话并获取用户信息
$stmt = $db->prepare("
SELECT u.id, u.username, u.is_admin
FROM sessions s
JOIN users u ON s.user_id = u.id
WHERE s.id = ?
");
$stmt->bind_param("s", $session_id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if (!$user) {
die("Invalid session.");
}
if (!$user['is_admin']) {
die("Forbidden: You don't have permission to access this page.");
}
// 如果通过验证,显示后台内容
echo "Welcome to the admin dashboard, " . htmlspecialchars($user['username']) . "!";
?>
---
4. 登出逻辑
<?php
session_start();
include 'db.php'; // 数据库连接文件
if (isset($_COOKIE['session_id'])) {
$session_id = $_COOKIE['session_id'];
// 删除会话记录
$stmt = $db->prepare("DELETE FROM sessions WHERE id = ?");
$stmt->bind_param("s", $session_id);
$stmt->execute();
// 清除 cookie
setcookie('session_id', '', time() - 3600, '/');
}
echo "Logged out successfully.";
?>
---
关键点说明
1. 会话管理:
会话 ID (session_id) 是随机生成的,攻击者无法轻易猜测。
会话信息保存在服务端数据库中,不依赖客户端数据。
2. 权限校验:
后端根据 session_id 查询用户权限 (is_admin),而非直接信任客户端传递的 is_admin 值。
3. 密码存储:
使用 password_hash() 和 password_verify() 安全地处理密码。
4. Cookie 安全性:
设置 HttpOnly 和 Secure 标志,防止 XSS 和中间人攻击。
SameSite=Strict 防止 CSRF 攻击。
5. 输入验证:
使用 prepared statements 防止 SQL 注入。
避免直接输出用户输入内容,使用 htmlspecialchars() 进行输出过滤。
---
运行逻辑示例
1. 用户登录:
用户提供用户名和密码。
系统验证密码并生成安全的 session_id。
session_id 存储在服务端数据库,并通过 cookie 传递给客户端。
2. 后台访问:
用户发起请求时,系统验证 session_id 的合法性。
根据数据库中用户的 is_admin 字段判断权限。
3. 权限不足:
如果用户 is_admin 为 0,拒绝访问后台。
---
总结
通过以上方式修复漏洞,可以确保即使攻击者篡改 cookie,也无法获得管理员权限,因为权限验证完全在后端进行,依赖于服务端的安全存储和验证逻辑。