您当前的位置:首页 > 电脑百科 > 安全防护 > 软件工具

安全同学讲Maven间接依赖场景的仲裁机制

时间:2022-09-22 15:16:47  来源:企鹅号  作者:阿里技术

一 背景

为什么想写此文

去年的Log4j-core的安全问题,再次把供应链安全推向了高潮。在供应链安全的场景,蚂蚁集团在静态代码扫描平台-STC和资产威胁透视平台-哈勃这2款产品的联动合作下,优势互补,很好的解决了直接依赖和间接依赖的场景。

但是由于STC是基于事前,受限于扫描效率存在遗漏的风险面,而哈勃又是基于事后,存在修复时间上的风险。基于此,笔者尝试寻找一种方式可以同时解决2款产品的短板。笔者尝试研究了一下Maven是如何处理一个项目中的直接依赖和间接依赖的,并且在遇到相同依赖时,Maven是如何进行抉择的,这里的如何抉择其实就是Maven的仲裁机制。带着这些问题,笔者尝试调研了Maven的源码和做了一些本地的测试实验。总结了这篇文章。

坐标是什么?

在空间坐标系中,我们可以通过xyz表示一个点,同样在Maven的世界里,我们可以通过一组GAV在依赖的世界里明确表示一个依赖,比如:

: com.alibaba 一般是公司的名称

: fastjson 项目名称

: 1.2.24 版本号

影响依赖的标签都有哪些

1.

直接引入具体的依赖信息。注意是不在标签内的情况。如果是在内的情况,请参考2号标签。

2.

只声明但不发生实际引入,作为依赖管理。依赖管理是指真正发生依赖的时候,再去参考依赖管理的数据。

这样使用dependency的时候,可以缺省version。

另外还可以管控所有的间接依赖,即使间接依赖声明了version,也要被覆盖掉。

3.

声明自己的父亲,Maven的继承哲学跟JAVA很类似,因为Maven本身也是用Java实现的,满足单继承。

一旦子pom继承了父pom,那么会把父pom里的,等等属性都继承过来的。当然如果在继承的过程中,出现一样的元素,也是子去覆盖父亲,和Java类似。

继承时,会分类继承。dependencies继承dependencies,dependencyManagement里的依赖管理只能继承dependencyManagement范围内的依赖管理。

每一个pom文件都会有一个父亲,即使不声明Parent,也会默认有一个父亲。和Java的Object设计哲学类似。后面在源码分析中我们还会提到。

4.

代表当前自己的项目的一个属性的集合。

properties仅仅代表属性的声明,一个属性声明了,和他是否被引用并无关系。我完全可以声明一系列不被人使用的属性。

依赖的作用域都有哪些

一个依赖在引入的时候,是可以声明这个依赖的作用范围的。比如这个依赖只对本地起作用,比如只对测试起作用等等。作用域一共有compile,provided,system,test,import,runtime 这几个值。

简单总结一下:

compile和runtime会参与最后的打包环节,其余的都不会。compile可以不写。

test只会对 src/test目录下的测试代码起作用。

provided是指线上已经提供了这个Jar包,打包的时候不需要在考虑他了,一般像servlet的包很多都是provided。

system和provided没什么太大的区别。

import只会出现在dependencyManagement标签内的依赖中,是为了解决Maven的单继承。引入了这个作用域的话,maven会把此依赖的所有的dependencyManagement内的元素加载到当前pom中的,但不会引入当前节点。如下图,并不会引入fastjson作为依赖管理的元素,只是会把fastjson文件定义的依赖管理引入进来。

二 单个Pom树的依赖竞争

Pom文件本质

一个Pom文件的本质就是一棵树。

在人的视角来观察一个Pom文件的时候,我们会认为他是一个线状的一个依赖列表,我们会认为下图的Pom文件抽象出来的结果是C依赖了A,B,D。但我们的视角是不完备的,Maven的视角来看,Maven会把这一个Pom文件直接抽象成一个依赖树。Maven的视角能看到除了ABD之外的节点。而人只能看到ABD三个节点。

既然是在一棵树上,那么相同的节点就必然会存在竞争关系。这个竞争关系就是我们提到了仲裁机制。

Maven仲裁机制原则

1.依赖竞争时,越靠近主干的越优先。

2.单颗树在依赖在竞争时(dependencies)(注意:不是dependencyManagement里的dependencies):

当deep=1,即直接依赖。同级是靠后优先。

当deep>1,即间接依赖。同级是靠前优先。

3.单颗树在依赖管理在竞争时(注意:是dependencyManagement里的dependencies)是靠前优先的。

4.maven里最重要的2个关系,分别是继承关系和依赖关系。我们所有的规律都应该只从这2个关系入手。

下图中分别是2个子pom文件(方块代表依赖的节点,A-1 表示A这个节点使用的是1版本,字母代表节点,数字代表版本)。

左边这个子pom生成的树依赖了 D-1,D-2和D-5。满足依赖竞争原则1,即越靠近树的左侧越优先的原则,所以D-5会竞争成功。

但是B-1和B-2同时都位于树的同一深度,并且深度为1,由于B-2更加靠后,所以B-2会竞争成功。

右边的子pom生成的树依赖了 D-1和D-2,并且位于同一深度,但由于D-1和D-2是属于间接依赖的范围,deep大于1,所以是靠前优先,那么也就是D-1会竞争成功。

常见场景

看到这里,想必大家已经了解了Maven的仲裁原则。但是在实际的工作中,光有原则还需要在代码中可以灵活的运用才能有属于自己的理解,这里笔者准备了5个场景,每个场景对应的答案都在后面,大家阅读时,可以自己尝试用Maven的原则来去推理,看看有没有哪里不符合预期的情况。

场景一 难度(*)

场景描述

主POM里有这个属性为1.2.24。

父亲是spring-boot-starter-parent-3.13.0。父亲里的是1.2.77。

并且在主pom中,消费了这个属性。

那么针对主POM这颗树,他最终会是使用哪一个fastjson呢?

场景示例

结构图

场景二 难度(**)

在同一个主POM或者子POM中的dependencies中同时使用了Fastjson,第一个声明了1.2.24的版本,第二个声明了1.2.25版本。那么针对主POM或者子pom这棵树,最终会选择fastjson 1.2.24还是1.2.25呢?

场景示例

结构图

场景三 难度(***)

下图中左图为主POM文件内的dependencyManagement里的fastjson为1.2.77,这个时候子POM中显示声明自己的版本1.2.78。那么针对子POM这颗树,子POM会选择听从父命还是遵从内心呢?

场景示例

结构图

场景四 难度(****)

主POM的dependenciesFastjson:1.2.24主POM的dependencymanagentFastjson:1.2.77

主POM的父亲(springboot)的dependenciesFastjson 1.2.78

子POM里的dependenciesFastjson 1.2.25

这种情况下针对子pom来说,他会选择4个版本中的哪一个呢?

场景示例

结构图

场景五 难度(*****)

主POM的dependenciesFastjson:1.2.24主POM的dependencymanagentFastjson:1.2.77

主POM的父亲(springboot)的dependenciesFastjson 1.2.78

子POM里的dependencies 不写version

场景五跟场景四整体没有差别,只是将子pom的dependencies的版本进行缺省。

这种情况下针对子pom来说,针对子pom,他会选择3个版本中的哪一个呢?

场景示例

结构图

答案

场景一

1.2.24会最终生效。

因为子会继承父亲的属性,但是由于自己有这个属性,那么则覆盖!

继承一定会伴随着覆盖的,这个设计在编程语言中还是比较普遍的。

场景二

1.2.25会最终生效。

参考 单颗树在依赖在竞争时:当deep=1,即直接依赖。同级是靠后优先。

满足Maven的核心竞争依赖策略!

场景三

1.2.78最终会生效。

一个项目里的dependencyManagement只能对不声明version的dependency和间接依赖有效!

场景四

1.2.25会最终生效。这个比较复杂。

〇: 首先根据父子的继承关系,1.2.24会覆盖掉1.2.78。所以78版本淘汰

一: 由于一个项目里的dependencyManagement只能对不声明version的dependency和间接依赖有效,所以

1.2.77无法对1.2.25起作用。

二: 由于父子的继承关系,1.2.25会覆盖掉1.2.24.

所以最终1.2.25胜出!

场景五

1.2.77会最终生效。

〇: 首先根据父子的继承关系,1.2.24会覆盖掉1.2.78。所以78版本淘汰

一: 由于一个项目里的dependencyManagement是可以对不声明的version起作用,所以子pom的版本为1.2.77

二: 由于父子的继承关系,1.2.77会覆盖掉1.2.24.

所以最终1.2.77胜出!

三 多个Pom树合并打包

多棵树构建顺序原则

现在的项目一般都是多模块管理,会存在非常多的pom文件。多棵树的情况下每棵树的出场顺序都是事先已经被计算好的。

这个功能在Maven的源码中是一个叫Reactor(反应堆)实现的。它主要做了一件事情就是决定一个项目中,多个子pom谁先进行build的顺序,这个出厂顺序很重要,在合并打包时,往往决定了最终谁会在多个pom之间胜出的问题。

Reactor的原则

多棵树(多个子pom)构建的顺序是按照被依赖方的要在前,依赖方在后的原则。

项目要保证这里是不能出现循环依赖的。

Reactor的原则图解

如下图子pom1 在被子pom2和子pom3同时依赖,所以子pom1最先被构建,子pom3没有人被依赖,所以最后构建。

SpringBoot Fatjar打包的策略

SpringBoot 打包会打成一个Fatjar,所有的依赖都会放在BOOT-INF/lib/目录下。SpringBoot的打包是越靠后的构建pom越优先,因为一般会把springboot的打包插件放在最不被依赖的module里(比如上图里的Pom3)。(SpringBoot的打包插件一般放在bootstrap pom里,这个名字可以我们自己起,一般都是依赖关系最靠上的module。在多模块管理的springboot应用内,bootstrap往往是最不被依赖的那个module。)

子pom3最后参与构建,而且SpringBoot打包插件一般打的就是这个module。所以最终进入到SpringBoot打包产物的有A-2,B-2,E-2,F-2和D-1。因为A-2和B-2相比于其他几个相同节点更靠近树的主干。E-2和F-2也是同理。这个规律体感上是靠后优先了,因为靠后的树天然更加靠近主干。

四 仲裁机制在Maven源码中的实现

以Maven的3.6.3版本的源码进行分析,我们尝试分析Maven中对依赖处理的几处原则,方能从源码的层面上正向的证明仲裁机制的准确性。另外从源码上也可以看出一些Maven上的机制为什么是这样,而不是单单的他的机制是什么样。因为笔者相信,任何机制都无法保证与时俱进下的先进性,所以笔者认为上文中提到的所有的仲裁机制有一天可能会发生变化,这些结论并非最重要,而是如何调研这些结论更为重要!

Maven是如何实现出继承并且相同属性子覆盖父的

Maven中有2条非常重要的主线。一个是依赖,另一个就是继承。Maven在源码中实现继承大体如下。在下图中使用readParent进行对父亲的模型获取之后,便让自己陷入这个循环中。唯一可以出去这个循环的方式就是追不到父亲为止。并且把每次取到模型数据放到linega这个对象当中。下图中最下面的assembleInheritance我们看他消费了linega这个对象,目的就是完成真实的继承和覆盖。

在assembleInheritance中我们会发现一个很有意思的现象,lingage是倒着进行遍历,并且是从倒数第二个元素开始,这正是上文中我们提到了的Maven的一个设计哲学。Maven认为这个世界上所有的pom文件都存在一个父亲,类似Java的Object。这里便是对这个哲学处理的一个浅逻辑。

另外Maven自上而下的去遍历,更加方便自己去实现相同的元素子覆盖父的能力,这也是笔者认为在编码上的一个小心思。

Reactor反应堆在源码中的实现

上文中我们还提到了一个非常重要的概念,就是反应堆。反应堆直接决定了各个子pom是如何决定构建顺序的。在Maven的源码中,他是在getProjectsForMavenReactor函数中进行实现的。并且我们从下图中也可以看到,Maven的反应堆是不能解决循环依赖的,他直接捕获了这种异常!

真正实现反应堆算法的是在ProjectSorter的构造函数中通过Dag进行实现的。Dag(有向无环图)和广度优先搜索是解决依赖场景是一个很好的方式。

在有向无环图中通过每次挑选出入度为0的节点,再删除该节点和此节点的相邻边,不断重复上述步骤。就可以高效率的计算出DAG上的所有节点的依赖顺序,Maven也正是用到了这个思路。

从这个源码的视角也可以解释为什么Maven必须要保证每一个子pom之前不能出现循环依赖。

同一个Pom文件内dependency 后声明的优先的实现

在处理Dependencies时,Maven并没有对此进行特殊处理,是直接使用的Map的方式进行覆盖的。关于这里为什么这么设计,笔者并不清楚。笔者曾一度猜测这么设计是为了让开发同学更好的编写,因为靠后优先往往符合大部分人的编码习惯。但是在这里我们看到了作者的一行注释,意思大概是说,这样设计是为了向后兼容Maven2.x,因为Maven2.x 是不会去校验一个文件是否只存在一个同GA的唯一依赖。所以后面的maven的版本应该也是延续了这种风格。

当循环进行处理到1.2.25的时候,依然进行对normalized这个map进行put操作导致了 key值相同的情况下的覆盖。

五 安全视角应如何避免间接依赖

分析

作为安全同学,笔者更希望的是针对这种多module的Maven项目可以梳理出一个经验,怎样去避免间接依赖的问题。

经过上面的分析,我们可以得出3条结论:

1.子pom声明版本在安全视角是非常危险的,子pom不应该显示声明版本。

由于子pom会继承主pom的元素,并且在继承的时候会出现覆盖的场景。那么针对CE或者SpringBoot打包时,有可能出现子pom的build的顺序位置天然非常有优势,容易造成子pom的版本进入最终的打包产物。

2.主POM的dependencyManagent可以管控到 间接依赖 和 不显示声明version的直接依赖。

3.主POM的dependencies不能出现危险版本。否则子pom天然的继承了这个危险版本参与打包。

结论

以上几条同时满足,便可以解决间接依赖的问题。

即:

针对SpringBoot而言,子pom不应该显示声明版本,主Pom的dependencyManagent应该管控安全版本的依赖,并且主pom不能出现危险版本。(主Pom dependencies强行写上安全版本更佳,这样可以避免掉依赖的父亲里存在残留的不安全的依赖)

六 最后

Maven的源码地址

https://archive.Apache.org/dist/maven/maven-3/

我是怎么分析的

本人在本地针对SpringBoot,做多轮测试。在根目录下执行mvn clean package即可!

另外就是尝试在源码中找到这里的实现,这样更能加深理解!

常用的分析命令

0. mvn clean package -DSkipTest 直接进行打包,进行结果分析

1. mvn dependency:tree 会把整个的maven的树形结构输出

2.mvn help:effective-pom -Dverbose 这个命令输出的信息更加完整,输出的是effectivepom



Tags:Maven   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
使用Maven和Tomcat创建并部署Java应用
引言本文分享使用Maven和Tomcat创建、构建和部署简单的Java Web应用程序的过程。Maven是个强大的构建自动化工具,可以帮助管理项目依赖关系并自动化构建任务,而Tomcat是被广泛...【详细内容】
2023-12-11  Search: Maven  点击:(171)  评论:(0)  加入收藏
教你如何选择Gradle和Maven
在软件开发领域,构建工具对于自动化编译、测试和打包代码过程至关重要。Gradle 和 Maven 是当今最流行的两种构建工具,每种工具都有自己的一套功能和优点。Gradle 是一种现代...【详细内容】
2023-10-08  Search: Maven  点击:(298)  评论:(0)  加入收藏
聊聊 Maven 最佳实践
本文翻译自国外论坛 medium,原文地址:https://medium.com/@AlexanderObregon/maven-best-practices-tips-and-tricks-for-java-developers-438eca03f72bMaven 简介Maven 是一...【详细内容】
2023-09-09  Search: Maven  点击:(325)  评论:(0)  加入收藏
10个Java项目中必备的Maven插件
介绍Maven是Java开发人员必备的构建工具和项目管理工具。它采用简单的项目对象模型(POM),实现Java应用程序的自动化构建、测试和部署。使用Maven插件,开发人员能够进一步扩展工...【详细内容】
2023-08-17  Search: Maven  点击:(321)  评论:(0)  加入收藏
Maven进阶学习指南
前言当我们在开发项目时,有时需要用到外部依赖组件,例如当我们需要Json序列化的时候需要用到FastJson组件,我们可以通过下载对应jar包加载到项目中。但当一个大的项目同时需要...【详细内容】
2023-06-25  Search: Maven  点击:(273)  评论:(0)  加入收藏
java开发人你用的maven构建还是gradle
Gradle和Maven都是Java项目构建工具,它们之间最主要的区别在于以下几点: 构建脚本语言:Maven使用XML作为构建脚本语言,而Gradle使用Groovy语言,Groovy是一种基于Java的动态语言。...【详细内容】
2023-05-16  Search: Maven  点击:(451)  评论:(0)  加入收藏
记一次内网maven私服初始化方法
背景 纯内网环境,无法连互联网 待初始化maven私服在内网,无法通过配置代理完成初始化 通过nexus部署私服,初始化maven依赖和npm依赖初始化思路 通过nexus管理端上传组件功能上...【详细内容】
2023-02-27  Search: Maven  点击:(271)  评论:(0)  加入收藏
Maven从入门到精通 pringBoot搭建SSM项目的完整过程 !
01、新建一个springboot项目 选择依赖和版本 SpringBoot整合lomboklombok是一个快速生成javabean的一个工具工程和生成日志的工具。未来给我们提供了很大的遍历。需要在ide...【详细内容】
2023-02-09  Search: Maven  点击:(183)  评论:(0)  加入收藏
安全同学讲Maven间接依赖场景的仲裁机制
一 背景为什么想写此文去年的Log4j-core的安全问题,再次把供应链安全推向了高潮。在供应链安全的场景,蚂蚁集团在静态代码扫描平台-STC和资产威胁透视平台-哈勃这2款产品的联...【详细内容】
2022-09-22  Search: Maven  点击:(339)  评论:(0)  加入收藏
Maven依赖的作用域你到底用对了没有
Maven是目前Java开发主要使用的依赖管理构建工具之一,但是很多人在引用依赖的时候直接引用坐标,而没有考虑依赖的作用范围,结果导致出现这样和那样的问题。今天胖哥就来说明一...【详细内容】
2022-07-26  Search: Maven  点击:(406)  评论:(0)  加入收藏
▌简易百科推荐
如何使用免费网络安全工具Canary Tokens查明黑客何时访问了您的文件?
译者 | 布加迪审校 | 重楼担心您的个人文件被黑客窃取吗?Canary Tokens是一款免费且易于使用的工具,可以快速部署。如果黑客打开您的文件,它就会通知您。什么是Canary Tokens?Ca...【详细内容】
2024-03-26    51CTO  Tags:Canary Tokens   点击:(13)  评论:(0)  加入收藏
如何用ChatGPT分析恶意软件
译者 | 陈峻审校 | 重楼自从我们进入数字化时代以来,恶意软件就一直是计算机应用系统的“心腹大患”。事实上,每一次技术进步都会为恶意行为者提供更多的工具,使得他们的攻击行...【详细内容】
2023-12-12    51CTO  Tags:恶意软件   点击:(201)  评论:(0)  加入收藏
NGFW:网络工程师必备防火墙
在网络技术飞速发展的今天,防火墙已成为网络工程师必备的防御工具之一。而其中,下一代防火墙(NGFW)凭借其高效、智能的防护特点,更是成为了业界的明星产品。本文将详细介绍NGFW的...【详细内容】
2023-12-12    历史痕迹  Tags:防火墙   点击:(132)  评论:(0)  加入收藏
如何保护应用?Web应用防火墙详细解读
如今,多云环境、API安全功能扩展、合作伙伴集成即时可用、可用性和可视化增强以及提高自动化程度已经成为基本要求。伴随企业应用架构的迁移,在用户端,需要在部署环境不断扩展...【详细内容】
2023-12-06  科技信息分析者    Tags:防火墙   点击:(135)  评论:(0)  加入收藏
八个优秀开源内网穿透工具
内网穿透(NAT穿透)是一种将本地网络服务暴露给互联网的一种技术。这种技术可以很好地解决许多局域网内的资源共享。采用路由的方式将一台计算机变成一个“路由器”,将公共的网...【详细内容】
2023-11-29  andflow  微信公众号  Tags:内网穿透   点击:(213)  评论:(0)  加入收藏
十个最佳免费 Linux 防火墙工具
概述防火墙是保护网络边界的关键。防火墙会阻止敏感端口并过滤传入和传出流量,以阻止恶意连接并确保不会发生未经请求的数据交换。在自由开源软件的世界里,有很多防火墙解决方...【详细内容】
2023-11-28  雪竹聊技术  今日头条  Tags:防火墙   点击:(173)  评论:(0)  加入收藏
防火墙与杀毒软件有什么区别?
硬件防火墙:系统是嵌入式的系统。一般开源的较多。硬件防火墙是通过硬件和软件的组合来达到隔离内外部网络的目的。软件防火墙:一般寄生在操作系统平台。软件防火墙是通过纯...【详细内容】
2023-11-16    天铭互联网  Tags:防火墙   点击:(215)  评论:(0)  加入收藏
ngrok-内网穿透神器,开发人员必备
概述ngrok是一款开源的网络服务,能够为在本地运行的网络应用提供公开的、基于互联网的URL。这使得网络开发人员可以将自己正在开发的网页或者API暴露到互联网上,方便进行演示...【详细内容】
2023-10-07  阿拉尔的阳光  今日头条  Tags:内网穿透   点击:(102)  评论:(0)  加入收藏
“二次约会”间谍软件分析报告:网络攻击西北工业大学 美国相关人员真实身份被锁定
近日,国家计算机病毒应急处理中心和360公司对名为“二次约会”(SecondDate)的“间谍”软件进行了技术分析,该“间谍”软件针对基于FreeBSD、Linux、Sun Solaris、Juniper JunOS...【详细内容】
2023-09-14    央视新闻客户端  Tags:间谍软件   点击:(302)  评论:(0)  加入收藏
DNSBin:一款功能强大的DNS与服务器安全测试工具
关于DNSBinDNSBin是一款功能强大的DNS与服务器安全测试工具,该工具可以通过DNS来测试数据泄露,并在目标环境部署了严苛网络安全限制的场景下帮助广大研究人员测试远程代码执...【详细内容】
2023-09-08    FreeBuf.COM  Tags:DNSBin   点击:(245)  评论:(0)  加入收藏
站内最新
站内热门
站内头条