记一次执行顺序问题导致的SQL注入绕过
字数 1305 2025-08-19 12:41:44

SQL注入绕过:执行顺序问题分析与防御

0x00 背景与漏洞概述

本案例展示了一个由于执行顺序问题导致的SQL注入绕过场景。目标应用使用MyBatis进行SQL交互,部分业务接口通过order by ${_parameter} desc实现排序功能,这种动态SQL拼接方式存在SQL注入风险。

关键漏洞代码

order by ${_parameter} desc  // 直接拼接用户输入,存在SQL注入风险

0x01 现有防御措施分析

应用通过过滤器(Filter)对用户参数进行SQL注入检查:

过滤器实现

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) 
    throws IOException, ServletException {
    
    // 获取请求参数
    Map<String, Object> paramsMaps = new TreeMap<>();
    if ("POST".equals(req.getMethod().toUpperCase())) {
        String body = requestWrapper.getBody();
        paramsMaps = JSONObject.parseObject(body, TreeMap.class);
    } else {
        // GET请求处理
    }

    // 校验SQL注入
    for (Object o : paramsMaps.entrySet()) {
        Map.Entry entry = (Map.Entry) o;
        Object value = entry.getValue();
        if (value != null) {
            boolean isValid = checkSqlInject(value.toString(), servletResponse);
            if (!isValid) return;
        }
    }
    chain.doFilter(requestWrapper, servletResponse);
}

SQL注入检查方法

private static final String SQL_REGX = ".*(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute)\\b).*";

private boolean checkSqlInject(String value, ServletResponse servletResponse) throws IOException {
    if (null != value && value.matches(SQL_REGX)) {
        // 拦截并返回错误
        return false;
    }
    return true;
}

0x02 拦截器实现分析

应用还存在一个拦截器(Interceptor),在其preHandle方法中使用Jsoup对所有用户输入进行HTML净化:

拦截器实现

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 对请求参数进行HTML净化
    for (String key : request.getParameterMap().keySet()) {
        String value = request.getParameter(key);
        value = sanitizeInput(value); 
        request.getParameterMap().replace(key, value);
    }
    return true;
}

public static String sanitizeInput(String strHtml) {
    String cleaned = "";
    if (StringUtil.isNotBlank(strHtml)){
        cleaned = Jsoup.clean(strHtml, whitelist);
        return cleaned;
    }
    return cleaned;
}

0x03 执行顺序分析

Java Web请求处理流程

  1. 过滤器(Filter):Servlet容器级别,最先执行
  2. 拦截器(Interceptor):在DispatcherServlet中处理,Controller之前执行
  3. Controller:业务逻辑处理
  4. Service:业务服务层

具体调用链

  1. Tomcat的StandardWrapperValve#invoke()创建FilterChain
  2. ApplicationFilterFactory.createFilterChain创建并执行过滤器链
  3. 所有过滤器执行完毕后,调用servlet.service(request, response)
  4. Spring MVC的DispatcherServlet处理请求
  5. getHandlerExecutionChain中添加并执行拦截器
  6. 执行Controller方法

0x04 绕过原理

由于过滤器先于拦截器执行,攻击者可构造特殊输入绕过SQL注入检查:

  1. 输入:selec</script>t
  2. 过滤器检查:不匹配SQL_REGX正则(因为被</script>分隔)
  3. 拦截器处理:Jsoup清理HTML标签后变为select
  4. 最终SQL执行:order by select desc,成功注入

示例代码

public static void main(String[] args) throws Exception {
    String value = "selec</script>t";
    System.out.println("用户输入:"+value);
    
    // 过滤器检查
    if (null != value && value.matches(SQL_REGX)) {
        throw new Exception("SQL注入拦截");
    }
    
    // 拦截器处理
    String cleaned = Jsoup.clean(value, whitelist);
    System.out.println("处理后:"+cleaned);  // 输出: select
}

0x05 防御建议

  1. 参数化查询:避免直接拼接SQL,使用预编译语句

    order by #{parameter} desc  // 使用#{}而非${}
    
  2. 防御措施执行顺序:确保净化操作在检查之前执行

  3. 完善正则检测

    • 添加对HTML标签的检测
    • 使用更严格的关键字匹配模式
  4. 统一防御层:将安全措施集中到同一层(如全部放在过滤器或拦截器)

  5. 白名单校验:对于order by字段,只允许特定字段名

  6. 日志监控:记录所有拦截的请求,便于分析攻击尝试

0x06 其他潜在问题

  1. JSON请求处理:过滤器与Controller的JSON解析可能存在差异
  2. 请求方法转换:GET请求在特定注解下可转为POST请求
  3. 多阶段处理:参数在不同处理阶段可能被多次编码/解码

0x07 总结

本案例展示了由于安全措施执行顺序不当导致的SQL注入绕过。关键在于:

  • 理解Filter和Interceptor的执行顺序差异
  • 净化操作应在检查之前执行
  • 防御措施需要全面考虑各种输入变形
  • 优先使用参数化查询等根本解决方案

通过全面分析执行流程和安全措施的交互,可以有效预防此类安全问题。

SQL注入绕过:执行顺序问题分析与防御 0x00 背景与漏洞概述 本案例展示了一个由于执行顺序问题导致的SQL注入绕过场景。目标应用使用MyBatis进行SQL交互,部分业务接口通过 order by ${_parameter} desc 实现排序功能,这种动态SQL拼接方式存在SQL注入风险。 关键漏洞代码 0x01 现有防御措施分析 应用通过过滤器(Filter)对用户参数进行SQL注入检查: 过滤器实现 SQL注入检查方法 0x02 拦截器实现分析 应用还存在一个拦截器(Interceptor),在其preHandle方法中使用Jsoup对所有用户输入进行HTML净化: 拦截器实现 0x03 执行顺序分析 Java Web请求处理流程 过滤器(Filter) :Servlet容器级别,最先执行 拦截器(Interceptor) :在DispatcherServlet中处理,Controller之前执行 Controller :业务逻辑处理 Service :业务服务层 具体调用链 Tomcat的 StandardWrapperValve#invoke() 创建FilterChain ApplicationFilterFactory.createFilterChain 创建并执行过滤器链 所有过滤器执行完毕后,调用 servlet.service(request, response) Spring MVC的 DispatcherServlet 处理请求 在 getHandlerExecutionChain 中添加并执行拦截器 执行Controller方法 0x04 绕过原理 由于过滤器先于拦截器执行,攻击者可构造特殊输入绕过SQL注入检查: 输入: selec</script>t 过滤器检查:不匹配 SQL_REGX 正则(因为被 </script> 分隔) 拦截器处理:Jsoup清理HTML标签后变为 select 最终SQL执行: order by select desc ,成功注入 示例代码 0x05 防御建议 参数化查询 :避免直接拼接SQL,使用预编译语句 防御措施执行顺序 :确保净化操作在检查之前执行 完善正则检测 : 添加对HTML标签的检测 使用更严格的关键字匹配模式 统一防御层 :将安全措施集中到同一层(如全部放在过滤器或拦截器) 白名单校验 :对于order by字段,只允许特定字段名 日志监控 :记录所有拦截的请求,便于分析攻击尝试 0x06 其他潜在问题 JSON请求处理 :过滤器与Controller的JSON解析可能存在差异 请求方法转换 :GET请求在特定注解下可转为POST请求 多阶段处理 :参数在不同处理阶段可能被多次编码/解码 0x07 总结 本案例展示了由于安全措施执行顺序不当导致的SQL注入绕过。关键在于: 理解Filter和Interceptor的执行顺序差异 净化操作应在检查之前执行 防御措施需要全面考虑各种输入变形 优先使用参数化查询等根本解决方案 通过全面分析执行流程和安全措施的交互,可以有效预防此类安全问题。