IDE插件开发_代码审计之多项拼接型SQLi-2
字数 1542 2025-08-15 21:33:52

IDE插件开发之多项拼接型SQL注入代码审计

一、背景与概述

本文基于陌陌安全开源的momo-code-sec-inspector-java项目,介绍如何开发IDE插件来检测Java代码中的多项拼接型SQL注入漏洞(SQLi)。

漏洞检测规则列表

该项目包含多种安全规则的检测能力,以下是部分规则列表:

编号 规则名称 修复建议 一键修复
1001 多项式拼接型SQL注入漏洞 T
1002 占位符拼接型SQL注入漏洞 T
1003 Mybatis注解SQL注入漏洞 T T
1004 Mybatis XML SQL注入漏洞 T T
... ... ... ...

二、开发准备

2.1 知识储备

  1. Java漏洞代码示例:参考java_sec_code项目提供的漏洞代码示例
  2. 代码审计模板:可从官方示例中获取模板
  3. PSI理解
    • PSI(Program Structure Interface)是IDEA插件开发中的核心概念
    • PSI负责解析文件并创建语法和语义代码模型
    • 类比:PSI文件是一棵树(.java文件),树枝是PSI element

2.2 工具准备

  1. PsiViewer插件:辅助理解代码中重写方法的选择,可视化PSI element
  2. 官方SDK文档:JetBrains官方提供的插件开发文档

三、项目结构与审计流程

3.1 项目结构

插件通过plugin.xml配置文件定义功能类及相关描述:

<extensions defaultExtensionNs="com.intellij">
  <localInspection 
    language="JAVA" 
    groupPath="Java" 
    groupName="MomoSec" 
    enabledByDefault="true" 
    level="ERROR" 
    bundle="com.immomo.momosec.bundle.InspectionBundle" 
    key="polyadic.expression.sqli.name" 
    implementationClass="com.immomo.momosec.lang.java.rule.momosecurity.PolyadicExpressionSQLi" />
</extensions>

3.2 代码审计流程

  1. plugin.xml读取功能类及描述
  2. 功能类继承IDEA相关API(如LocalInspectionTool)
  3. 遍历正在浏览/编辑的Java文件,匹配问题代码
  4. 高亮显示问题代码,提供详情和修复建议
  5. 支持使用ALT + ENTER自动修复

四、多项拼接型SQL注入检测实现

4.1 漏洞代码示例

Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
String sql11 = String.format("select * from users where username = '%s'", username);
String sql12 = "fgfd" + "ds" + username;
ResultSet rs = statement.executeQuery(sql);

4.2 检测类实现

public class PolyadicExpressionSQLi extends BaseSQLi {
    public static final String MESSAGE = InspectionBundle.message("polyadic.expression.sqli.msg");
    private static final String QUICK_FIX_NAME = InspectionBundle.message("polyadic.expression.sqli.fix");
    private final ShowHelpCommentQuickFix showHelpCommentQuickFix = 
        new ShowHelpCommentQuickFix(QUICK_FIX_NAME, SQL_INJECTION_HELP_COMMENT);

    @NotNull
    @Override
    public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
        return new JavaElementVisitor() {
            @Override
            public void visitPolyadicExpression(PsiPolyadicExpression expression) {
                List<PsiExpression> exps = MoExpressionUtils.deconPolyadicExpression(expression);
                if (exps.isEmpty()) {
                    return;
                }
                
                String expStr = exps.stream()
                    .map(item -> MoExpressionUtils.getText(item, true))
                    .collect(Collectors.joining());
                
                if (isSql(expStr)) {
                    List<String> sql_segments = new ArrayList<>();
                    StringBuilder sb = new StringBuilder();
                    boolean hasVar = false;
                    
                    for (PsiExpression exp : exps) {
                        if (isSqliCareExpression(exp)) {
                            String s = MoExpressionUtils.getLiteralInnerText(exp);
                            if (s == null) {
                                if (!sb.toString().isEmpty()) {
                                    sql_segments.add(sb.toString());
                                    sb.delete(0, sb.length());
                                }
                                if (!MoExpressionUtils.isText(exp)) {
                                    hasVar = true;
                                }
                            } else {
                                sb.append(s);
                            }
                        } else {
                            sb.append(末段要 drop ,不用查);
                        }
                    }
                    
                    if (sql_segments.isEmpty() || Boolean.FALSE.equals(hasVar) 
                        || !hasEvalAdditive(sql_segments)) {
                        if (hasPlaceholderProblem(expStr) && !ignoreMethodName(expression)) {
                            holder.registerProblem(expression, 
                                PlaceholderStringSQLi.MESSAGE, 
                                ProblemHighlightType.GENERIC_ERROR_OR_WARNING, 
                                showHelpCommentQuickFix);
                        }
                        return;
                    }
                    
                    if (!ignoreMethodName(expression)) {
                        holder.registerProblem(expression, 
                            MESSAGE, 
                            ProblemHighlightType.GENERIC_ERROR_OR_WARNING, 
                            showHelpCommentQuickFix);
                    }
                }
            }
        };
    }
    
    private boolean hasPlaceholderProblem(String content) {
        return placeholderPattern.matcher(content).find() 
            && isSql(content) 
            && hasEvalAdditive(content, placeholderPattern);
    }
}

4.3 关键点解析

  1. 变量说明

    • MESSAGE:匹配规则后显示的问题信息
    • QUICK_FIX_NAME:修复方案显示名称
    • showHelpCommentQuickFix:修复建议提示
  2. 核心方法

    • buildVisitor:访问PSI文件的父类方法,返回PSI element
    • holder:当前查看/编辑文件对象(LocalInspection类的实例)
  3. PSI元素处理

    • 拼接的String元素类型为PsiPolyadicExpression
    • 重写visitPolyadicExpression方法来寻找拼接SQLi
  4. 检测逻辑

    • 使用MoExpressionUtils.deconPolyadicExpression将拼接语句split为多段
    • 判断是否为SQL语句(isSql方法)
    • 检查拼接部分是否包含变量
    • 根据检查结果注册问题(holder.registerProblem)

4.4 SQL语句识别

BaseSQLi.java中定义SQL识别模式:

private static final Pattern sqlPattern = 
    Pattern.compile("^\\s*(select|delete|update|insert)\\s+.*?(from|into|set)\\s+.*?where.*", 
    Pattern.CASE_INSENSITIVE);

protected boolean isSql(String str) {
    return sqlPattern.matcher(str).find();
}

五、总结

  1. 本文介绍了多项拼接型SQL注入的检测方法,核心是识别PsiPolyadicExpression类型的元素
  2. 占位符型SQL注入的类型是PsiMethodCallExpression,与多项拼接型不同
  3. 通过PSI解析和模式匹配,可以有效识别代码中的不安全SQL拼接
  4. 插件提供了问题高亮、描述信息和快速修复建议

后续可以扩展实现其他类型漏洞的检测,如占位符型SQL注入、MyBatis相关注入等。

IDE插件开发之多项拼接型SQL注入代码审计 一、背景与概述 本文基于陌陌安全开源的 momo-code-sec-inspector-java 项目,介绍如何开发IDE插件来检测Java代码中的多项拼接型SQL注入漏洞(SQLi)。 漏洞检测规则列表 该项目包含多种安全规则的检测能力,以下是部分规则列表: | 编号 | 规则名称 | 修复建议 | 一键修复 | |------|---------|---------|---------| | 1001 | 多项式拼接型SQL注入漏洞 | T | | | 1002 | 占位符拼接型SQL注入漏洞 | T | | | 1003 | Mybatis注解SQL注入漏洞 | T | T | | 1004 | Mybatis XML SQL注入漏洞 | T | T | | ... | ... | ... | ... | 二、开发准备 2.1 知识储备 Java漏洞代码示例 :参考 java_sec_code 项目提供的漏洞代码示例 代码审计模板 :可从官方示例中获取模板 PSI理解 : PSI(Program Structure Interface)是IDEA插件开发中的核心概念 PSI负责解析文件并创建语法和语义代码模型 类比:PSI文件是一棵树(.java文件),树枝是PSI element 2.2 工具准备 PsiViewer插件 :辅助理解代码中重写方法的选择,可视化PSI element 官方SDK文档 :JetBrains官方提供的插件开发文档 三、项目结构与审计流程 3.1 项目结构 插件通过 plugin.xml 配置文件定义功能类及相关描述: 3.2 代码审计流程 从 plugin.xml 读取功能类及描述 功能类继承IDEA相关API(如 LocalInspectionTool ) 遍历正在浏览/编辑的Java文件,匹配问题代码 高亮显示问题代码,提供详情和修复建议 支持使用 ALT + ENTER 自动修复 四、多项拼接型SQL注入检测实现 4.1 漏洞代码示例 4.2 检测类实现 4.3 关键点解析 变量说明 : MESSAGE :匹配规则后显示的问题信息 QUICK_FIX_NAME :修复方案显示名称 showHelpCommentQuickFix :修复建议提示 核心方法 : buildVisitor :访问PSI文件的父类方法,返回PSI element holder :当前查看/编辑文件对象(LocalInspection类的实例) PSI元素处理 : 拼接的String元素类型为 PsiPolyadicExpression 重写 visitPolyadicExpression 方法来寻找拼接SQLi 检测逻辑 : 使用 MoExpressionUtils.deconPolyadicExpression 将拼接语句split为多段 判断是否为SQL语句( isSql 方法) 检查拼接部分是否包含变量 根据检查结果注册问题( holder.registerProblem ) 4.4 SQL语句识别 在 BaseSQLi.java 中定义SQL识别模式: 五、总结 本文介绍了多项拼接型SQL注入的检测方法,核心是识别 PsiPolyadicExpression 类型的元素 占位符型SQL注入的类型是 PsiMethodCallExpression ,与多项拼接型不同 通过PSI解析和模式匹配,可以有效识别代码中的不安全SQL拼接 插件提供了问题高亮、描述信息和快速修复建议 后续可以扩展实现其他类型漏洞的检测,如占位符型SQL注入、MyBatis相关注入等。