洞态IAST对SpringBoot项目扫描API接口的实现方法

admin 2025年2月24日13:01:02评论17 views字数 4427阅读14分45秒阅读模式

1

前言

洞态 IAST 团队开发项目API导航功能。本文以SpringBoot项目为例,对其进行所有API接口的扫描。

2

环境

jdk11

SpringBoot2.x

3

原理

1.找到SpringBoot所有接口的思路

了解 SpringMVC 的同学都知道,Spring 框架把接口写在 Controller 层,获取每一个 Controller 中的接口理论上可以获取项目的全部接口。因为 Spring 独特的 Ioc 机制,所有的接口和 service 都存在一个 map 容器内,我们只要找到这个 map 容器即可。

RequestMappingHandlerMapping类是在DispatcherServlet的初始化过程中自动加载的,默认会自动加载所有实现HandlerMapping接口的bean。

所以在 DispatcherServlet 初始化结束后获取到 RequestMappingHandlerMapping 对象,我们所需要的 map 就在这个对象中。

RequestMappingHandlerMapping 的初始化过程:RequestMappingHandlerMapping 会遍历所有 bean,如果 bean 实现带有注解 @Controller 或者 @RequestMapping 则进一步调用 detectHandlerMethods 处理,处理逻辑大致就是根据 @RequestMapping 配置的信息,构建 RequestMappingInfo,然后注册到 MappingRegistry 中。

2.实现通过 RequestMappingHandlerMapping 获取 SpringBoot 所有接口

要实现通过 RequestMappingHandlerMapping 获取SpringBoot所有接口,首先要获取到 RequestMappingHandlerMapping 这个 Bean。获取Bean通过 ApplicationContext 对象的getBean()方法。

获取 ApplicationContext 对象的方式有很多,洞态 IAST 使用字节码插桩,所以只需要找到 ApplicationContext 初始化完成的源码位置即可。获取到 ApplicationContext 对象之后就能得到 RequestMappingHandlerMapping 对象,该对象的getHandlerMethods()方法即可获取到所有的接口了。

 RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);Map<RequestMappingInfo, HandlerMethod> methodMap = mapping.getHandlerMethods();
洞态IAST对SpringBoot项目扫描API接口的实现方法

3.字节码插桩

DongTai-agent-java 使用 ASM 框架进行字节码插桩,ASM 是一个 Java 字节码操控框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

DongTai-agent-java 会对大部分类打标签,将不同标签的类进行不同的字节码插桩。上文中已经描述过我们需求的类,就是有 ApplicationContext 对象的类,只要获取到 ApplicationContext 对象,我们就可以取到我们想要的 API 接口。

FrameworkServlet是Spring web框架的基本 servlet 实现类,通过 JavaBean 的方式集成了ApplicationContext,使用该类的好处是可以确保ApplicationContext已经加载完毕,可以获取到完整的API信息,而且触发该类的方式很简单,当应用启动之后访问项目就可以出发到该类,这时就可以向Server端发送报告。

4.向Server端发送报告

新建报告模型 ApiDataModel,将 Server 端所需的信息封装一个模型。

public class ApiDataModel {    private String url;    private String method;    private String clazz;    private Map<String,String>[] parameters;    private String returnType;    private String file;    private String controller;}

生成 json 报告

 private static String createReport(List<ApiDataModel> apiList) {        JSONObject report = new JSONObject();        JSONObject detail = new JSONObject();        JSONArray apiData = new JSONArray();        report.put(ReportConstant.REPORT_KEY, ReportConstant.REPORT_API);        report.put(ReportConstant.REPORT_VALUE_KEY, detail);        detail.put(ReportConstant.AGENT_ID,AgentRegisterReport.getAgentFlag());        detail.put(ReportConstant.API_DATA, apiData);        for (ApiDataModel apiDataModel:apiList             ) {            JSONObject api = new JSONObject();            apiData.put(api);            api.put(ReportConstant.API_DATA_URI,apiDataModel.getUrl());            api.put(ReportConstant.API_DATA_METHOD,apiDataModel.getMethod());            api.put(ReportConstant.API_DATA_CLASS,apiDataModel.getClazz());            List<Map<String, String>> parameters = apiDataModel.getParameters();            JSONArray parametersJson = new JSONArray();            api.put(ReportConstant.API_DATA_PARAMETERS,parametersJson);            for (Map<String,String> parameter:parameters                 ) {                JSONObject parameterjson = new JSONObject();                parametersJson.put(parameterjson);                parameterjson.put(ReportConstant.API_DATA_PARAMETER_NAME,parameter.get(ReportConstant.API_DATA_PARAMETER_NAME));                parameterjson.put(ReportConstant.API_DATA_PARAMETER_TYPE,parameter.get(ReportConstant.API_DATA_PARAMETER_TYPE));                parameterjson.put(ReportConstant.API_DATA_PARAMETER_ANNOTATION,parameter.get(ReportConstant.API_DATA_PARAMETER_ANNOTATION));            }            api.put(ReportConstant.API_DATA_RETURN,apiDataModel.getReturnType());            api.put(ReportConstant.API_DATA_FILE,apiDataModel.getFile());            api.put(ReportConstant.API_DATA_CONTROLLER,apiDataModel.getController());        }        return report.toString();    }

向 Server 端发送报告

  public static void sendApi(MethodEvent event, AtomicInteger invokeIdSequencer) {        if (!isSend) {        Object applicationContext = event.returnValue;        createClassLoader(applicationContext);        loadApplicationContext();        String invoke = null;        try {            invoke = (String) getAPI.invoke(null, applicationContext);            EngineManager.sendNewReport(invoke);        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InvocationTargetException e){            e.printStackTrace();        }        isSend = true;        }    }

4

总结

Spring 框架对 Javaweb 项目的管理使得获取 Spring 项目的所有 API 比较容易实现,但是对于没有使用框架原生的 Javaweb 项目获取API就比较困难,各位师傅如果有好的方法可以在评论区留言或者私信联系。

火线安全

洞态IAST对SpringBoot项目扫描API接口的实现方法
火线安全是基于社区的云安全公司,主要运营洞态IAST、火线安全平台和火线Zone云安全社区。通过自主研发的自动化测试工具和海量的白帽安全专家,助力企业解决应用生命全周期的安全风险。

洞态IAST对SpringBoot项目扫描API接口的实现方法

原文始发于微信公众号(火线安全平台):洞态IAST对SpringBoot项目扫描API接口的实现方法

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月24日13:01:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   洞态IAST对SpringBoot项目扫描API接口的实现方法https://cn-sec.com/archives/874615.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息