Web安全核心:越权漏洞原理、渗透测试实战与防御方案详解

Web安全核心:越权漏洞原理、渗透测试实战与防御方案详解

1. 项目概述:为什么越权漏洞是渗透测试的“必考题”?

干了这么多年渗透测试,我越来越觉得,越权漏洞(Broken Access Control)就像渗透测试里的“内功心法”。它不像SQL注入、XSS那样有炫酷的payload,也不像文件上传那样能直接getshell,但它几乎无处不在,而且一旦被利用,造成的危害往往直接、严重。很多甲方客户,甚至是开发团队,对这个漏洞的认知都停留在“不就是改个ID吗”的层面,但实际情况要复杂和危险得多。

简单来说,越权漏洞就是系统没有对用户的操作权限做严格的校验,导致用户可以执行超出其应有权限的操作。比如,一个普通用户能查看其他用户的订单,一个部门员工能审批公司CEO的请假申请,甚至一个外部访客能访问到数据库里其他应用的数据。这听起来是不是比单纯的“改ID”要吓人得多?在最新的OWASP Top 10中,访问控制失效(Broken Access Control)已经跃升至首位,这足以说明它在实际安全威胁中的分量。

这篇文章,我就结合自己这些年挖洞和做代码审计的经验,把水平越权、垂直越权、目录越权、SQL跨库查询越权这几种常见的越权类型,掰开了、揉碎了讲清楚。我会用大量真实的测试场景和代码片段来举例,告诉你它们是怎么产生的,渗透测试时怎么去发现和验证,以及开发层面到底该怎么防。无论你是刚入门的安全爱好者,还是在准备面试的准安全工程师,相信这篇近万字的详解都能让你对越权漏洞有一个系统而深刻的认识。

2. 越权漏洞的核心原理与分类拆解

要理解越权,首先得明白现代Web应用权限控制的基本模型。通常,权限控制发生在两个层面:身份认证(Authentication)授权(Authorization)。认证解决“你是谁”的问题(比如登录),授权解决“你能干什么”的问题。越权漏洞,本质上就是授权环节的校验逻辑出现了缺陷或缺失。

根据用户能够越权访问的“对象”和“范围”不同,我们通常将其分为以下几类。理解这些分类,是精准测试和有效修复的前提。

2.1 水平越权:同级别用户间的“串门”

水平越权(Horizontal Privilege Escalation),也叫作“平行越权”,是最常见的一种。它指的是攻击者能够访问或操作与其拥有相同权限级别的其他用户的资源。

核心特征:权限级别不变,但操作对象从“自己”变成了“别人”。典型场景

  1. 查看他人信息:通过修改用户ID、订单号、文章ID等参数,查看其他用户的私密信息。例如,将https://example.com/user/profile?uid=1001中的uid参数改为1002,就能看到用户1002的个人资料。
  2. 操作他人数据:修改、删除其他用户创建的内容。比如,在博客平台删除他人的评论(deleteComment?comment_id=12345),而这个comment_id属于另一个用户。
  3. 使用他人凭证/令牌:虽然不常见,但如果会话令牌(Session Token)或API密钥与用户资源绑定不严格,也可能导致水平越权。

漏洞根源:服务端在处理请求时,只验证了用户是否登录(认证),但没有验证当前登录用户是否有权操作这个特定的资源ID。代码层面通常表现为:后端根据前端传来的参数(如uid,order_id)直接进行数据库查询或操作,而没有将参数值与当前会话中的用户身份进行绑定校验。

# 漏洞代码示例(Flask框架) @app.route('/api/order/<int:order_id>') def get_order(order_id): # 只检查了用户是否登录,没有检查订单是否属于该用户 if 'user_id' not in session: return jsonify({'error': 'Unauthorized'}), 401 order = db.session.query(Order).get(order_id) # 直接根据参数查询 return jsonify(order.to_dict())

修复思路:在数据查询或操作前,增加“资源所属权”校验。确保请求的参数所对应的资源,其所有者(owner)字段必须与当前登录用户的ID匹配。

# 修复后代码 @app.route('/api/order/<int:order_id>') def get_order(order_id): user_id = session.get('user_id') if not user_id: return jsonify({'error': 'Unauthorized'}), 401 # 查询时关联用户ID order = db.session.query(Order).filter_by(id=order_id, user_id=user_id).first() if not order: return jsonify({'error': 'Order not found or access denied'}), 404 return jsonify(order.to_dict())

2.2 垂直越权:普通用户的“皇帝梦”

垂直越权(Vertical Privilege Escalation)的危害性通常更大。它指的是低权限用户(如普通用户)能够执行本应属于高权限用户(如管理员)的操作。

核心特征:用户权限级别被非法提升。典型场景

  1. 访问管理后台:普通用户通过直接输入管理员后台URL(如/admin//wp-admin/)或功能接口(如/api/admin/user/list),在未登录或已登录但非管理员身份的情况下,成功访问。
  2. 调用管理员API:通过抓包修改请求,调用仅限管理员使用的功能接口。例如,普通用户发送一个修改用户角色的POST请求到/api/user/change_role
  3. 越权使用功能:普通用户使用需要特定权限(如VIP、审核员)才能使用的功能,例如发布全站公告、审核内容等。

漏洞根源:服务端对功能或页面的访问控制粒度太粗,或者完全依赖前端隐藏按钮/菜单来控制权限。后端在处理请求时,没有校验执行该操作所必需的角色(Role)权限点(Permission)

// 漏洞代码示例(Spring Boot框架) @GetMapping("/admin/deleteUser") public String deleteUser(@RequestParam Long userId) { // 缺失权限校验:没有检查当前用户是否是“ADMIN”角色 userService.deleteById(userId); return "User deleted successfully"; }

修复思路:实施基于角色(RBAC)或基于权限的访问控制。在每个需要权限控制的路由或方法上,显式声明所需的权限。可以使用注解、拦截器或中间件来实现。

// 修复后代码:使用Spring Security注解 @PreAuthorize("hasRole('ADMIN')") // 声明需要ADMIN角色 @GetMapping("/admin/deleteUser") public String deleteUser(@RequestParam Long userId) { userService.deleteById(userId); return "User deleted successfully"; }

2.3 目录越权:穿越路径的“任意门”

目录越权,更专业的叫法是目录遍历(Directory Traversal)路径遍历(Path Traversal)。它属于一种特殊的越权,用户通过操纵文件路径参数,访问或操作服务器文件系统中本应受限的目录或文件。

核心特征:通过“../”等序列突破预设的目录限制。典型场景

  1. 文件下载漏洞download.php?file=../../../../etc/passwd。通过构造../回退到根目录,读取系统敏感文件。
  2. 文件读取/包含漏洞view.php?page=../../../config/database.php。读取Web目录外的配置文件。
  3. 文件上传漏洞:虽然上传点通常会限制目录,但如果文件名处理不当,可能通过包含路径遍历序列的文件名(如../../../shell.php)将文件写入非预期目录。

漏洞根源:服务端在处理文件路径参数时,直接拼接用户输入,没有进行规范化处理和安全校验。没有将访问范围限制在预期的安全目录(如Web应用的uploads/目录)内。

// 漏洞代码示例(PHP) $file = $_GET['file']; // 用户可控,例如传入 "../../../etc/passwd" readfile('/var/www/html/uploads/' . $file); // 直接拼接,造成遍历

修复思路

  1. 白名单校验:只允许访问预设的、安全的文件名列表。
  2. 规范化与剥离:对输入的文件名进行规范化处理,然后检查是否包含路径遍历序列(../,..\,%2e%2e%2f等),并予以拒绝或剥离。
  3. 目录锁定:使用basename()函数获取文件名部分,或使用chroot、设置工作目录等方式,将文件操作锁定在特定目录内。
// 修复思路1:白名单 $allowed_files = ['report1.pdf', 'report2.pdf']; $file = $_GET['file']; if (!in_array($file, $allowed_files)) { die('Invalid file request.'); } // 修复思路2:剥离路径并锁定目录 $file = $_GET['file']; $file = basename($file); // 剥离任何目录路径,只保留文件名 $base_path = '/var/www/html/uploads/'; $real_path = realpath($base_path . $file); // 确保最终路径在基准路径内 if (strpos($real_path, $base_path) !== 0) { die('Access denied.'); } readfile($real_path);

2.4 SQL跨库查询越权:数据库层面的“跨界访问”

这是一种相对隐蔽但危害巨大的越权形式,常出现在数据库配置不当或SQL语句拼接存在缺陷的场景中。它允许攻击者利用一个应用的数据库查询权限,访问同一数据库服务器上其他应用(其他数据库)的数据。

核心特征:利用一个数据库连接,查询或操作其他数据库(Schema)。典型场景

  1. MySQL中的UNION查询跨库:当Web应用存在SQL注入点,且数据库用户拥有较高权限(如root或具有跨库权限的账户)时,攻击者可以通过UNION SELECT查询其他数据库的表。例如:... UNION SELECT 1, table_name FROM information_schema.tables WHERE table_schema='another_app_db' ...
  2. 直接使用数据库名限定符:在某些SQL语句中,如果用户输入被直接拼接且未过滤点号(.),攻击者可能构造如SELECT * from other_db.users的查询。
  3. 利用数据库特定功能:如SQL Server的OPENROWSETOPENDATASOURCE函数,可以执行跨服务器甚至跨实例的查询。

漏洞根源

  1. 应用层:存在SQL注入漏洞,且注入点可利用。
  2. 数据库层:连接数据库所使用的账户权限过高,被赋予了SELECTINSERT等操作其他数据库的权限。
  3. 配置层:数据库服务器未对不同应用进行严格的权限隔离(例如,多个应用共用同一个高权限数据库账户)。

渗透测试中的发现思路:当你发现一个SQL注入点时,除了获取当前数据库的信息,一定要尝试进行信息搜集:

  • 查询当前数据库用户:SELECT USER(), CURRENT_USER();
  • 查询用户权限:SELECT * FROM mysql.user WHERE User='current_user' \G(MySQL)
  • 尝试列举所有数据库:SELECT schema_name FROM information_schema.schemata;
  • 尝试进行跨库查询:UNION SELECT 1, CONCAT(table_schema, '.', table_name) FROM information_schema.tables WHERE table_schema != 'current_db'

修复思路

  1. 根治SQL注入:使用参数化查询(Prepared Statements)或ORM框架,从根本上杜绝SQL注入。
  2. 遵循最小权限原则:为每个Web应用创建独立的、低权限的数据库用户。该用户只拥有对其自身业务数据库的必要操作权限(如SELECT,INSERT,UPDATE,DELETE),且绝对不能拥有GRANT OPTIONFILEPROCESS等高危权限,尤其要禁止ALL PRIVILEGES ON *.*这种授权。
  3. 网络与实例隔离:在可能的情况下,为重要应用部署独立的数据库实例,实现物理或逻辑上的完全隔离。

3. 渗透测试实战:如何系统性地挖掘越权漏洞

知道了原理,接下来就是实战。在渗透测试中,发现越权漏洞需要系统性的方法和细致的观察。它不像扫描器扫漏洞那样自动化程度高,更多依赖于测试人员的手动测试和对业务逻辑的理解。

3.1 测试环境与工具准备

工欲善其事,必先利其器。除了必备的浏览器(用于手动测试),以下几类工具能极大提升效率:

  1. 代理抓包工具:这是核心中的核心。用于拦截、查看、修改所有HTTP/HTTPS请求和响应。

    • Burp Suite Professional/Community:行业标准,功能强大。Repeater、Intruder、Scanner模块在测试越权时非常有用。
    • OWASP ZAP:开源免费,功能同样全面,是Burp Suite的优秀替代品。
    • Fiddler/Charles:也不错,但在Web安全测试领域,前两者更专业。
  2. 浏览器插件

    • 浏览器开发者工具(F12):自带的网络(Network)控制台就是最基础的抓包工具。
    • EditThisCookie等Cookie编辑器:方便修改会话信息,测试垂直越权时可能用到。
    • HackBar:方便快速构造和发送Payload。
  3. 靶场环境:对于学习和练习至关重要。文中提到的Pikachu、DVWA、WebGoat、以及各种CTF靶场(如BugKu相关题目)都包含了经典的越权漏洞场景。

3.2 水平越权漏洞的挖掘流程

水平越权的测试思路非常直接:替换ID,观察响应

  1. 枚举所有带ID的参数:在测试过程中,留意所有出现在URL、POST Body、Cookie甚至HTTP头中的标识符。常见参数名包括:id,uid,user_id,order_id,doc_id,file_id,account,phone,email等。不常见的也可能自定义,如pid,cid,no等。

  2. 替换ID值

    • 递增/递减:如果你自己的用户ID是1001,尝试访问1000、1002等相邻ID。
    • 其他已知ID:如果系统其他地方泄露了其他用户的ID(如文章评论列表显示评论者ID),直接拿来测试。
    • 使用Burp Intruder进行爆破:对于数字型ID,可以设置Payload为数字序列进行批量测试。对于复杂ID(如UUID),如果存在规律或可预测性,也可以尝试。
  3. 观察响应差异

    • 状态码:返回200 OK很可能成功,403/404可能失败。但要注意,有些应用对越权请求也返回200,但内容是“无数据”或错误提示,需要仔细看响应体。
    • 响应内容:对比访问自己ID和其他ID返回的HTML、JSON数据。如果内容结构相同但数据不同,基本可以确定存在水平越权。
    • 响应长度:在Burp Intruder中,通过排序响应长度,可以快速发现那些返回了不同数据量的请求(可能成功了)。
  4. 测试增删改操作:不仅仅是查看(GET),更要测试修改(POST/PUT)和删除(DELETE)操作。例如,尝试修改他人的收货地址、删除他人的评论等。

实战技巧

  • 关注批量操作接口:像/api/batch_delete_messages?ids=[1,2,3]这样的接口,如果后端没有逐个校验每个id的所有权,就可能存在批量水平越权。
  • 注意间接引用对象:有时参数不是直接的用户ID,而是订单号、文章ID等。你需要判断这个资源对象是否与用户强绑定。修改订单号,看是否能访问他人订单。
  • 利用时间戳或弱随机数:有些系统使用时间戳或可预测的随机数作为资源标识符。如果算法被猜出,就可能遍历到他人的资源。

3.3 垂直越权漏洞的挖掘流程

垂直越权的关键在于发现那些“隐藏”的高权限功能入口,并尝试以低权限身份访问。

  1. 信息收集与功能枚举

    • 爬取站点地图:使用Burp Suite的爬虫(Spider)或OWASP ZAP的爬虫功能,尽可能多地发现网站目录和接口。同时,以管理员身份登录后再爬一次,对比两次结果,找出仅管理员可见的路径。
    • 分析前端代码:查看前端JavaScript代码,寻找被注释掉、被隐藏(display:none)或通过权限判断动态加载的管理功能URL或API端点。
    • 目录/文件爆破:使用工具如dirsearch,gobuster,ffuf,配合强大的字典(如SecLists中的目录字典),暴力猜解管理后台、API文档、配置文件等常见路径(如/admin/,/wp-admin/,/api/,/console/,/phpmyadmin/)。
  2. 接口与参数分析

    • 抓取所有请求:用代理工具记录普通用户操作的所有HTTP请求。
    • 寻找权限关键词:在请求URL、参数名、接口文档中寻找如admin,manage,delete,edit,config,setting,role,permission,root,super等词汇。
    • 修改请求方法:尝试将GET请求改为POST,或将POST改为PUT/DELETE,有时低权限路径支持高权限方法。
  3. 权限绕过尝试

    • 直接访问:在未登录或普通用户登录状态下,直接访问收集到的管理员URL。
    • 修改Cookie/Session/Token:如果系统使用自定义的Session标识角色(如role=user),尝试修改为role=admin。对于JWT Token,可以尝试解码后修改payload中的角色字段(但需要知道密钥才能重新签名,否则服务端校验会失败)。
    • 删除权限参数:有些请求会带一个isAdmin=true之类的参数,尝试删除或修改它。
    • 伪造Referer或Origin头:某些粗浅的权限校验可能依赖这些HTTP头。
  4. 测试功能边界:即使不能直接进入管理后台,也要测试普通用户功能中是否混入了高权限操作。例如,普通用户的“个人设置”页面,是否有一个隐藏的“修改系统配置”选项?或者,某个提交表单的API,是否也能接受本应只有管理员才能设置的参数(如user_role)?

实战技巧

  • 关注API路由设计:RESTful API设计中,类似/api/v1/users(普通用户管理自己) 和/api/v1/admin/users(管理员管理所有用户) 可能并存。尝试直接调用后者。
  • 利用逻辑缺陷:有些权限校验分两步:先检查是否登录,再在具体功能里检查角色。如果第一步通过后,第二步的校验因为代码bug被跳过,就可能造成垂直越权。
  • 测试“特权”功能:如密码重置、邮件发送、支付回调验证等。这些功能如果缺乏权限控制,可能被滥用于影响其他用户或系统状态。

3.4 目录遍历与SQL跨库查询的专项测试

这两类漏洞的测试方法更偏向于传统的漏洞利用,有相对固定的Payload。

目录遍历测试

  1. 寻找文件操作参数:关注file,path,page,include,load,document,template,pdf,image等参数名。
  2. 注入路径遍历序列
    • 基础Payload:../../../etc/passwd
    • 编码绕过:..%2f..%2f..%2fetc%2fpasswd(URL编码),..\..\..\windows\win.ini(Windows)
    • 空字节截断(针对旧系统):../../../etc/passwd%00.jpg
    • 绝对路径:/etc/passwd(如果未限制)
  3. 使用工具自动化:Burp Suite的Intruder可以加载包含各种路径遍历Payload的字典进行模糊测试。

SQL跨库查询测试

  1. 前提是发现SQL注入点:首先通过常规方法(布尔盲注、时间盲注、报错注入、联合查询)确认并利用SQL注入漏洞。
  2. 信息搜集:利用注入点获取当前数据库用户、权限和所有数据库名。
    • MySQL:SELECT user(), current_user();SELECT schema_name FROM information_schema.schemata;
    • MSSQL:SELECT user_name();SELECT name FROM master..sysdatabases;
  3. 尝试跨库查询
    • 如果当前用户权限足够,可以直接在联合查询中指定其他数据库名:UNION SELECT 1, table_name FROM other_database.information_schema.tables
    • 或者查询其他数据库的表内容:UNION SELECT 1, CONCAT(username, ':', password) FROM other_database.users
  4. 利用数据库特性:研究特定数据库(如MSSQL的OPENROWSET)的跨库查询语句,构造更复杂的利用。

4. 漏洞防御:从开发到运维的立体化方案

找到漏洞很重要,但知道如何修复和预防更重要。防御越权漏洞需要一个贯穿开发全生命周期的体系。

4.1 设计阶段:确立安全的权限模型

安全是设计出来的,不是测试出来的。在项目初期,架构师和开发负责人就必须明确权限模型。

  1. 采用成熟的权限模型

    • RBAC(基于角色的访问控制):这是最常用、最清晰的模型。定义好角色(如admin,user,vip),为角色分配权限,再将角色赋予用户。适合大多数后台管理系统。
    • ABAC(基于属性的访问控制):更细粒度、更灵活。根据用户属性(部门、级别)、资源属性(敏感等级、所有者)、环境属性(时间、IP)和操作来动态决定是否允许访问。适合复杂的企业级应用。
    • 最小权限原则:任何用户、进程或系统,只应拥有完成其任务所必需的最小权限。
  2. 明确数据所有权:在数据库设计时,对于需要区分所有者的数据表(如orders,articles),必须包含一个明确的“所有者ID”字段(如user_id)。这是防御水平越权的数据基础。

  3. 设计安全的API:RESTful API设计要清晰。使用名词复数表示资源集合(/users),使用HTTP方法表示操作(GET查,POST增,PUT改,DELETE删)。权限校验应该基于“资源+操作”的组合。

4.2 编码阶段:实现可靠的访问控制校验

这是防御漏洞最关键的环节,需要在每一个可能涉及权限的地方添加校验。

  1. 水平越权防御(所有权校验)

    • 代码模板化:对于任何根据ID查询用户个人资源的操作,形成固定的代码模式:“先取当前用户ID -> 查询时带上用户ID条件 -> 判断查询结果是否存在”。
    • 使用ORM框架的优势:像Django的ORM、Laravel的Eloquent、Spring Data JPA等,在定义模型关系时,可以很自然地实现“用户-订单”的一对多关系。查询时,直接从当前用户对象关联查询其订单(current_user.orders.filter(id=order_id)),这样框架会自动加上所有权限制,更安全。
    • 避免直接使用前端传来的ID进行删除/更新DELETE FROM orders WHERE id = ?是危险的,必须加上AND user_id = ?
  2. 垂直越权防御(角色/权限校验)

    • 使用统一的权限校验中间件/拦截器/过滤器:不要在每一个Controller或Action里写重复的if (!isAdmin()) { return error; }。应该在一个统一的入口进行拦截。例如:
      • Spring Security的@PreAuthorize,@Secured注解。
      • Django的@permission_required,@login_required装饰器。
      • Express.js的授权中间件。
    • 后端定义权限列表:所有需要权限控制的功能点,在后端有一个统一的清单(如权限字符串:user:delete,order:export)。前端菜单根据用户权限动态渲染,但后端接口必须逐一校验
    • 避免依赖隐藏前端控件:绝对不能认为“把管理员按钮在前端隐藏了”就是安全。所有权限判断必须在服务端执行。
  3. 目录遍历防御

    • 白名单机制:如果功能只允许下载固定文件,维护一个允许的文件名白名单。
    • 规范化与校验:使用编程语言提供的安全函数(如Python的os.path.normpath(),PHP的realpath()),将路径规范化后,检查其是否以允许的基准目录开头。
    • 使用数据库存储文件信息:将上传的文件保存在一个难以猜测的目录(如用UUID重命名),在数据库中记录文件ID和存储路径的映射。用户下载时通过文件ID查询数据库获取真实路径,而不是直接传递路径参数。
  4. SQL注入与跨库查询防御

    • 强制使用参数化查询(Prepared Statements):这是唯一被证明能有效防御SQL注入的方法。它让SQL代码和数据分离,数据库不会将输入解释为SQL指令。
    • 使用ORM框架:好的ORM框架默认使用参数化查询。
    • 遵循最小权限原则配置数据库用户:为Web应用创建专属数据库用户,并严格限制其权限。
      -- 错误示范:权限过大 GRANT ALL PRIVILEGES ON *.* TO 'webapp'@'%'; -- 正确示范:权限最小化 CREATE DATABASE myapp_db; CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'StrongPassword!'; GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'myapp_user'@'localhost'; FLUSH PRIVILEGES;

4.3 测试与运维阶段:持续的安全保障

  1. 代码审计与同行评审:在代码合并前,进行安全代码评审,重点关注所有涉及用户输入、数据库操作、文件操作和权限判断的代码块。
  2. 自动化安全测试(SAST/DAST)
    • 静态应用安全测试(SAST):使用工具(如SonarQube, Checkmarx, Fortify)扫描源代码,寻找可能存在越权风险的代码模式(如直接拼接SQL、未校验所有权的查询)。
    • 动态应用安全测试(DAST):使用漏洞扫描器(如Burp Suite Enterprise, Acunetix, Nessus)对运行中的应用进行黑盒测试,尝试发现越权漏洞。
  3. 渗透测试与红蓝对抗:定期聘请专业的安全团队或组织内部红队进行模拟攻击,这是发现逻辑漏洞(包括高级越权)最有效的手段。
  4. 日志审计与监控:在服务端记录详细的访问日志和操作日志,包括用户ID、请求路径、参数、时间戳和操作结果。通过日志分析平台监控异常模式,例如:
    • 同一个用户短时间内访问大量不同的用户ID。
    • 普通用户账号尝试访问/admin/*路径。
    • 大量的403 Forbidden404 Not Found响应,可能表明有攻击者在进行目录枚举或越权尝试。
  5. WAF(Web应用防火墙)规则:虽然WAF难以防御复杂的业务逻辑越权,但可以配置规则来拦截明显的路径遍历攻击(如请求中包含大量的../序列)和常见的SQL注入攻击模式,作为一道补充防线。

5. 常见问题与排查技巧实录

在实际的渗透测试和代码审计中,总会遇到一些似是而非的情况和棘手的坑。这里分享一些我踩过的坑和总结的技巧。

5.1 水平越权测试中的“坑”与技巧

  1. 问题:修改ID后返回“数据不存在”或“404”,这是漏洞吗?

    • 分析:不一定。这可能是正常的设计(资源不存在),也可能是开发人员的一种“安全”返回——对所有非法的资源访问都返回“未找到”,以避免泄露信息(这被称为“混淆的拒绝”)。虽然安全性稍好,但仍属于访问控制缺陷,因为攻击者无法区分“资源不存在”和“无权访问”。
    • 技巧:对比测试。用高权限用户(如管理员)访问同一个ID。如果管理员能访问到,而普通用户返回“不存在”,那基本可以确定是水平越权,只是错误信息被隐藏了。
  2. 问题:系统使用了复杂的、非连续的ID(如UUID),怎么测试?

    • 技巧
      • 信息泄露:从其他功能点寻找泄露的ID。例如,在用户列表、文章列表、评论列表中,通常会显示其他用户的ID或用户名。
      • 预测与规律:有些UUID可能是基于时间戳或MAC地址生成的,可能存在一定的规律性,但难度较大。
      • 关注其他参数:水平越权不一定只通过user_id。手机号、邮箱、用户名,甚至订单号、邀请码都可能成为标识符。测试时要思维发散。
  3. 问题:请求里没有明显的ID参数,怎么办?

    • 技巧
      • 检查Cookie或Token:用户身份可能保存在Cookie或JWT Token中。尝试修改这些信息。例如,将Cookie中的userid=1001改为userid=1002
      • 检查HTTP头:有些应用会将用户信息放在自定义的HTTP头里,如X-User-Id
      • POST Body中的隐藏字段:查看表单提交的完整数据,可能有隐藏的<input type="hidden" name="uid" value="1001">

5.2 垂直越权测试中的难点与突破

  1. 问题:管理后台有二次密码或2FA(双因素认证),怎么绕过?

    • 分析:这是良好的安全实践。垂直越权测试的目标是“访问到需要高权限的功能入口或接口”。如果你能绕过登录直接进入后台页面(即使后面还需要二次验证),这本身就是一个中危漏洞,因为它降低了攻击门槛。你的测试报告可以指出:“攻击者可未授权访问管理后台登录页面,结合其他漏洞(如暴力破解、社会工程学)可能进一步获取系统控制权。”
  2. 问题:权限校验似乎很完善,每个接口都查了角色,还有办法吗?

    • 技巧:关注“功能组合”产生的越权。单个功能权限控制完好,但多个功能组合可能产生越权。例如:
      • 功能A:普通用户可以上传图片。
      • 功能B:管理员可以设置网站Logo(从已上传图片中选择)。
      • 漏洞:普通用户上传一张恶意图片,然后通过某种方式(如预测ID)让功能B将其设置为网站Logo,造成影响。
    • 这需要深入理解业务逻辑,也是自动化工具难以发现的地方。

5.3 开发人员常见的错误认知与纠正

  1. 误区:“我们用了RBAC框架,所以没越权问题。”

    • 纠正:RBAC框架只是提供了工具,如果配置错误或使用不当,漏洞依然存在。例如,给角色分配了过宽的权限;或者在代码中忘记为某个接口添加@PreAuthorize注解。
  2. 误区:“前端已经做了菜单和按钮的权限控制,后端简单判断登录就行。”

    • 纠正:这是最危险的认知之一。所有权限校验必须、无条件地在服务端执行。前端控制仅用于提升用户体验。攻击者可以轻松绕过前端,直接调用API。
  3. 误区:“我们的ID是加密的,改不了。”

    • 纠正:加密或混淆的ID(如/user/abc123def)只能增加一点攻击难度,不能代替服务端的所有权校验。只要这个ID对应着不同的资源,攻击者就可以通过其他途径(如分享链接)获取到其他用户的加密ID进行测试。安全性不能依赖“隐蔽”
  4. 误区:“我们定期改管理员路径,别人找不到。”

    • 纠正:安全通过混淆(Security by Obscurity)是无效的。管理员路径一旦在第一次部署或某次泄露中被发现,就失去了意义。正确的做法是做好强制身份认证和权限校验,即使路径被猜到,未授权用户也无法访问。

5.4 渗透测试报告中的越权漏洞描述要点

当你发现一个越权漏洞并要写入报告时,光说“存在水平越权”是不够的。一份优秀的报告应该让开发人员能快速理解并复现。

  1. 清晰定位:明确漏洞类型(水平/垂直/目录遍历)、涉及的URL、HTTP方法、参数。
  2. 步骤重现:提供从普通用户登录开始,到成功越权的完整、分步的操作步骤。最好附带截图或Burp Suite的请求/响应数据。
  3. 证明影响:展示越权操作成功后的结果。例如,成功查看他人隐私信息的页面截图,或成功调用管理员API的响应包。
  4. 风险评级:根据漏洞的利用难度、所需前置条件、可能造成的业务影响(数据泄露、篡改、删除)和影响范围(单个用户、全部用户)来评定风险等级(高危、中危、低危)。
  5. 修复建议:给出具体、可操作的代码修复方案,而不是笼统地说“加强校验”。参考本文前面“防御”部分的代码示例。

渗透测试挖洞就像一场攻防博弈,越权漏洞尤其考验测试者对业务逻辑的理解和耐心。它没有一键通杀的武器,需要你像一个侦探一样,仔细地观察每一个请求,思考每一个参数背后的含义。而对于开发者和架构师而言,建立起从设计到编码再到测试的完整防御体系,才是杜绝这类“逻辑漏洞”的根本之道。希望这篇超详细的解析,能成为你手中一把好用的“手术刀”,无论是用于攻击测试,还是用于构建更坚固的防御。