登录框之另类思考:来自客户端的欺骗
字数 1109 2025-08-18 11:37:23

客户端欺骗登录框漏洞分析与防御指南

0x01 漏洞背景

在Web应用安全测试中,登录框是常见但容易被忽视的攻击面。传统防护措施如风控系统、验证码、SSO登录和OAuth授权已经相对成熟,攻击者需要寻找新的突破点。

0x02 漏洞特征发现

通过Fuzz后台目录发现异常现象:

  • 所有请求返回状态码均为200
  • 不同页面的响应Size不同
  • 访问受限页面(如/system/user/index/)时被重定向到首页,但仍返回200状态码

0x03 正常鉴权流程

  1. 客户端向服务端发起请求
  2. 服务端全局过滤器判断访问权限
    • 权限不足时可能返回:
      • 200状态码+错误页面
      • 302跳转登录页
      • 403无权限
      • 500越权异常
  3. 权限足够则继续执行业务逻辑

0x04 异常流程分析

  1. 客户端发起请求
  2. 服务端接口直接响应(未做权限检查)
  3. 浏览器解析响应内容
  4. 前端JS检查Cookie中的权限标志,决定是否跳转

关键矛盾点

  • 返回200状态码但跳转登录页
  • 不同URL返回不同Size的响应内容

0x05 漏洞利用方法

脆弱点分析

  1. 缺乏服务端权限校验:依赖前端JS做权限判断
  2. 前端可篡改:Cookie标志、JS逻辑都可被修改

实际案例

案例一:前端鉴权绕过

  1. 通过F12查看源码,发现使用Ajax异步登录
  2. 分析发现登录成功跳转逻辑在前端JS中实现
  3. 直接访问后台接口返回200但被前端JS重定向
  4. 可篡改前端获取的RoleID等权限标识

案例二:拦截式攻击

  1. 直接访问受限接口返回HTML+JS跳转代码
  2. 拦截响应,删除location.href跳转逻辑
  3. 获取原始接口响应,可能包含敏感数据
  4. 构造请求直接调用业务接口

0x06 安全防护方案

基于角色的权限控制实现(Java示例)

项目结构

src/
  main/
    java/
      com/screw/
        controller/TestHello.java
        util/PrivilegeFilter.java
    resources/
    webapp/
      WEB-INF/
        privilege.properties
        web.xml

权限配置文件(privilege.properties)

admin=admin
user=admin,user
login=guest,user,admin

过滤器实现(PrivilegeFilter.java)

public class PrivilegeFilter implements Filter {
    private Properties properties = new Properties();
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String fileName = filterConfig.getInitParameter("privilegeFile");
        String realPath = filterConfig.getServletContext().getRealPath(fileName);
        try {
            properties.load(new FileInputStream(realPath));
        } catch(Exception e) {
            filterConfig.getServletContext().log("读取权限控制文件失败",e);
        }
    }
    
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        
        String requestUri = request.getRequestURI().replace(request.getContextPath(), "");
        String role = (String)request.getSession().getAttribute("role");
        role = role == null ? "guest" : role;
        
        boolean authen = false;
        for(Object obj : properties.keySet()) {
            String key = (String)obj;
            if(requestUri.indexOf(key) != -1) {
                if(((String) properties.get(key)).indexOf(role) != -1) {
                    authen = true;
                    break;
                }
            }
        }
        
        if(!authen) {
            throw new RuntimeException("您无权访问该页面。");
        }
        chain.doFilter(request, response);
    }
}

web.xml配置

<filter>
    <filter-name>PrivilegeFilter</filter-name>
    <filter-class>com.screw.util.PrivilegeFilter</filter-class>
    <init-param>
        <param-name>privilegeFile</param-name>
        <param-value>/WEB-INF/privilege.properties</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>PrivilegeFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

测试Controller

@Controller
public class TestHello {
    @RequestMapping(value = "/login", method=RequestMethod.GET)
    public String login() { return "login"; }
    
    @RequestMapping(value = "/login", method=RequestMethod.POST)
    public String login(String username, String passwd, HttpSession session) {
        if(username.equals("admin") && passwd.equals("123456")) {
            session.setAttribute("role", "admin");
        } else if (username.equals("user") && passwd.equals("123456")) {
            session.setAttribute("role", "user");
        } else {
            return "login";
        }
        return "index";
    }
    
    @RequestMapping("/admin/index")
    public String admin() { return "admin"; }
    
    @RequestMapping("/user/add")
    public String add() { return "add"; }
}

0x07 关键防护要点

  1. 服务端全局拦截:在请求到达业务逻辑前进行权限校验
  2. 基于角色的访问控制(RBAC)
    • 定义清晰的权限层级(admin/user/guest)
    • 配置文件管理URL-角色映射关系
  3. 统一错误处理
    • 权限不足时返回明确的错误(403)
    • 避免返回200状态码+前端跳转
  4. 会话管理
    • 权限标识存储在服务端Session中
    • 避免依赖前端可修改的标识(Cookie/JS变量)

0x08 总结

客户端欺骗漏洞的核心问题是权限校验的"前端化"。有效的防护必须:

  1. 在服务端实现全局权限拦截
  2. 建立完善的基于角色的访问控制机制
  3. 避免信任任何来自客户端的安全判断
  4. 采用统一、明确的无权限响应方式
客户端欺骗登录框漏洞分析与防御指南 0x01 漏洞背景 在Web应用安全测试中,登录框是常见但容易被忽视的攻击面。传统防护措施如风控系统、验证码、SSO登录和OAuth授权已经相对成熟,攻击者需要寻找新的突破点。 0x02 漏洞特征发现 通过Fuzz后台目录发现异常现象: 所有请求返回状态码均为200 不同页面的响应Size不同 访问受限页面(如 /system/user/index/ )时被重定向到首页,但仍返回200状态码 0x03 正常鉴权流程 客户端向服务端发起请求 服务端全局过滤器判断访问权限 权限不足时可能返回: 200状态码+错误页面 302跳转登录页 403无权限 500越权异常 权限足够则继续执行业务逻辑 0x04 异常流程分析 客户端发起请求 服务端接口直接响应(未做权限检查) 浏览器解析响应内容 前端JS检查Cookie中的权限标志,决定是否跳转 关键矛盾点 : 返回200状态码但跳转登录页 不同URL返回不同Size的响应内容 0x05 漏洞利用方法 脆弱点分析 缺乏服务端权限校验 :依赖前端JS做权限判断 前端可篡改 :Cookie标志、JS逻辑都可被修改 实际案例 案例一:前端鉴权绕过 通过F12查看源码,发现使用Ajax异步登录 分析发现登录成功跳转逻辑在前端JS中实现 直接访问后台接口返回200但被前端JS重定向 可篡改前端获取的RoleID等权限标识 案例二:拦截式攻击 直接访问受限接口返回HTML+JS跳转代码 拦截响应,删除 location.href 跳转逻辑 获取原始接口响应,可能包含敏感数据 构造请求直接调用业务接口 0x06 安全防护方案 基于角色的权限控制实现(Java示例) 项目结构 权限配置文件(privilege.properties) 过滤器实现(PrivilegeFilter.java) web.xml配置 测试Controller 0x07 关键防护要点 服务端全局拦截 :在请求到达业务逻辑前进行权限校验 基于角色的访问控制(RBAC) : 定义清晰的权限层级(admin/user/guest) 配置文件管理URL-角色映射关系 统一错误处理 : 权限不足时返回明确的错误(403) 避免返回200状态码+前端跳转 会话管理 : 权限标识存储在服务端Session中 避免依赖前端可修改的标识(Cookie/JS变量) 0x08 总结 客户端欺骗漏洞的核心问题是权限校验的"前端化"。有效的防护必须: 在服务端实现全局权限拦截 建立完善的基于角色的访问控制机制 避免信任任何来自客户端的安全判断 采用统一、明确的无权限响应方式