Crushftp 认证绕过漏洞(CVE-2025-2825)
Apache Pinot 认证绕过漏洞(CVE-2024-56325)
越权漏洞的防御需要从服务器端权限校验、角色访问控制、参数验证、会话管理等多维度入手,确保用户只能访问其被授权的资源和功能。
一、水平越权防御:用户数据隔离校验
核心思想:确保用户只能操作自己的数据,禁止跨用户访问。
1. 强制绑定用户ID与请求参数
在处理用户数据的接口中,必须校验请求中的用户ID/资源ID是否属于当前登录用户。
代码示例(Flask + SQLAlchemy):
from flask import Flask, request, jsonify
from flask_login import current_user # 假设使用Flask-Login管理用户会话
app = Flask(__name__)
@app.route('/api/user/<int:user_id>/info', methods=['GET'])
def get_user_info(user_id):
# 校验当前用户是否为管理员或请求的user_id属于自己
if not current_user.is_admin and user_id != current_user.id:
return jsonify({"error": "无权访问"}), 403
# 查询数据库(假设User模型有id、name等字段)
user = User.query.get(user_id)
if not user:
return jsonify({"error": "用户不存在"}), 404
return jsonify({"name": user.name, "email": user.email}), 200
2. 使用用户上下文自动过滤数据
在数据库查询层,自动将当前用户ID作为条件,避免手动拼接参数导致的遗漏。
代码示例(Django ORM):
@RestController
@RequestMapping("/admin")
@SecurityRequirement(name = "role-based-access")
public class AdminController {
// 仅管理员可访问
@GetMapping("/users")
@PreAuthorize("hasRole('ADMIN')")
public List<User> getAllUsers() {
return userService.getAll();
}
// 超级管理员可删除用户
@DeleteMapping("/users/{id}")
@PreAuthorize("hasRole('SUPER_ADMIN')")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
二、垂直越权防御:角色权限严格控制
核心思想:基于角色的访问控制(RBAC),确保低权限用户无法访问高权限功能。
1. 角色分层与权限注解
定义明确的角色(如`user`、`admin`、`super_admin`),并为每个接口设置访问权限。
代码示例(Spring Boot + Spring Security):
@RestController
@RequestMapping("/admin")
@SecurityRequirement(name = "role-based-access")
public class AdminController {
// 仅管理员可访问
@GetMapping("/users")
@PreAuthorize("hasRole('ADMIN')")
public List<User> getAllUsers() {
return userService.getAll();
}
// 超级管理员可删除用户
@DeleteMapping("/users/{id}")
@PreAuthorize("hasRole('SUPER_ADMIN')")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
2. 自定义权限校验中间件
在请求进入控制器前,通过中间件校验用户角色。
代码示例(Express.js + JWT):
const jwt = require('jsonwebtoken');
const { SECRET_KEY } = require('./config');
function requireRole(role) {
return (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).send('未提供令牌');
try {
const decoded = jwt.verify(token, SECRET_KEY);
if (decoded.role !== role) {
return res.status(403).send('权限不足');
}
req.user = decoded; // 将用户信息挂载到请求对象
next();
} catch (error) {
res.status(403).send('无效的令牌');
}
};
}
// 使用示例:仅管理员可访问
app.get('/admin/dashboard', requireRole('admin'), (req, res) => {
res.send('管理员面板');
});
三、参数校验与防篡改
核心思想:对用户输入的参数进行严格校验,防止非法参数绕过权限控制。
1. 禁止前端控制关键参数
关键参数(如`user_id`、`role`)必须由服务器端生成,禁止前端传递或修改。
反例(危险做法):
<!-- 前端传递role参数,可被篡改 -->
<form action="/api/update-role" method="post">
<input type="hidden" name="user_id" value="123">
<input type="hidden" name="role" value="user"> <!-- 可被修改为admin -->
</form>
正确做法:
前端仅传递必要的业务参数(如用户输入的内容),权限相关参数由服务器根据登录用户自动填充。
例如,修改用户资料时,前端无需传递`user_id`,服务器默认使用当前用户ID。
2. 参数格式与范围校验
对用户ID、资源ID等参数进行格式校验(如整数、UUID)和范围限制(如非负数)。
代码示例(Python + Pydantic):
from pydantic import BaseModel, PositiveInt
from fastapi import FastAPI, HTTPException
app = FastAPI()
class UserRequest(BaseModel):
user_id: PositiveInt # 强制校验为正整数
@app.get("/api/user/{user_id}")
def get_user(user_id: int):
# 使用Pydantic校验参数
try:
validated_data = UserRequest(user_id=user_id)
except ValueError:
raise HTTPException(status_code=400, detail="无效的user_id")
# 后续校验当前用户是否有权限访问validated_data.user_id
后续校验当前用户是否有权限访问validated_data.user_id
四、会话与令牌安全
核心思想:确保用户身份标识不可伪造,防止会话劫持导致越权。
1. JWT令牌的正确使用
使用HTTPS传输令牌,防止中间人攻击。
令牌中仅存储必要的用户信息(如用户ID、角色),敏感信息(如密码)绝不存入。
采用强签名算法(如HS256、RS256),避免令牌被篡改。
代码示例(生成与校验JWT,Node.js):
const jwt = require('jsonwebtoken');
// 生成令牌(登录时)
function generateToken(userId, role) {
return jwt.sign({ userId, role }, SECRET_KEY, { expiresIn: '1h' });
}
// 校验令牌(请求拦截时)
function verifyToken(token) {
try {
return jwt.verify(token, SECRET_KEY); // 验证签名并返回 payload
} catch (error) {
throw new Error('无效的令牌');
}
}
2. Cookie安全属性设置
设置`HttpOnly`、`Secure`、`SameSite`属性,防止XSS窃取Cookie和跨站请求伪造(CSRF)。
代码示例(Express.js设置Cookie):
res.cookie('sessionToken', token, {
httpOnly: true, // 禁止JavaScript访问Cookie
secure: true, // 仅通过HTTPS传输
sameSite: 'strict', // 防止跨站请求携带Cookie
maxAge: 86400000 // 有效期1天
});
五、权限动态校验与最小权限原则
核心思想:权限控制应细粒度到操作级别,而非仅角色级别。
1. 基于资源的访问控制(ABAC)
对每个资源(如文件、订单)设置访问策略,结合用户属性(如部门、职位)动态校验权限。
代码示例(伪代码,模拟文件访问权限):
def can_access_file(user, file_id):
# 规则:用户属于文件所有者或所在部门的管理员
file = get_file(file_id)
return user.id == file.owner_id or user.department == file.department and user.is_department_admin
2. 最小权限分配
普通用户仅授予基本操作权限(如查询自己的数据),禁止删除、修改等高危操作。
管理员权限细分:
普通管理员:仅能管理指定模块(如用户管理)。
超级管理员:拥有全局权限。
代码示例(权限列表校验,Django):
def has_permission(user, required_permission):
return required_permission in user.permissions.all() # 假设用户权限存储为列表
# 使用示例:删除用户需要delete_user权限
@login_required
def delete_user(request, user_id):
if not has_permission(request.user, 'delete_user'):
return HttpResponseForbidden("无删除权限")
# 执行删除逻辑
六、日志审计与异常监控
核心思想:记录权限相关操作,及时发现异常越权尝试。
1. 关键操作日志记录
记录用户ID、操作时间、请求参数、响应状态等信息。
代码示例(Python logging模块):
import logging
logger = logging.getLogger('access_log')
@app.route('/api/user/<int:user_id>', methods=['GET'])
def get_user(user_id):
logger.info(f"用户{current_user.id}尝试访问用户{user_id}")
# 权限校验逻辑...
2. 异常行为告警
通过监控工具(如ELK、Prometheus)设置规则:
当同一用户短时间内访问大量不同用户ID时触发告警。
低权限用户频繁请求高权限接口时触发告警。
七、避免常见误区
1. 依赖前端控制权限:
前端代码可被任意修改,权限校验必须完全在服务器端实现。
反例:通过JavaScript隐藏管理员按钮,但用户仍可直接请求管理员接口。
2. 硬编码权限参数:
禁止在URL或请求体中直接传递`role=admin`等参数,应通过会话或令牌隐式校验。
反例:
POST /api/delete-user HTTP/1.1
role: admin // 可被篡改
3. 忽略未认证用户的越权:
未登录用户可能通过公开接口尝试越权(如未授权访问公开数据中的敏感字段),需对匿名请求同样进行参数校验。
总结:防御越权的核心原则
1. 服务器端严格校验:所有权限逻辑必须在后端实现,禁止依赖前端或客户端。
2. 数据与权限绑定:用户操作的资源ID必须与当前用户强绑定(如`user_id=当前用户ID`)。
3. 最小权限与分层:角色权限细分,避免权限滥用;高风险操作(如删除、提权)需二次校验。
4. 动态校验而非静态判断:根据请求上下文(如资源所有者、操作时间)动态校验权限,而非仅依赖角色。