如何冻结Ruby标准库?防范恶意Gem篡改核心类
Ruby标准库类防护与供应链攻击应对方案
一、冻结标准库/核心类的具体实现
要阻止恶意Gem修改核心类或标准库类,有两种直接可行的思路:
1. 直接冻结类本身
Ruby的freeze方法可以让类进入不可修改状态,之后任何尝试重新打开类、添加/修改方法的操作都会抛出RuntimeError:
# 冻结单个核心类 String.freeze Array.freeze Net::HTTP.freeze # 批量冻结指定标准库类(需先加载信任库) ObjectSpace.each_object(Class) do |cls| # 跳过自定义类、信任库专属类 next if cls.name.start_with?('MyApp::') || cls.name.in?(['ActiveSupport::StringInquirer']) # 按需冻结目标标准库命名空间下的类 cls.freeze if cls.name.start_with?('Net::', 'File::', 'IO::') end
注意:必须先加载ActiveSupport等需要修改核心类的信任库,再执行冻结操作,否则合法库的正常扩展也会报错。
2. 用TracePoint监控类修改行为
如果不想完全冻结类,而是对非信任的修改发出警告或终止进程,可以用Ruby的TracePoint API监控类的打开操作:
# 自定义信任Gem白名单 trusted_gems = %w[activesupport rails nokogiri] TracePoint.new(:class) do |tp| # 定位调用方所属的Gem caller_gem = tp.binding.caller_locations.find do |loc| loc.path.include?('/gems/') end&.path&.split('/gems/')&.last&.split('/')&.first # 拦截非信任Gem对标准库类的修改 if caller_gem && !trusted_gems.include?(caller_gem) && tp.self.name.start_with?('Net::', 'String', 'Array') warn "[WARNING] 非信任Gem #{caller_gem} 尝试修改类 #{tp.self.name}" # 需要直接终止进程可启用下面这行 # raise "非法类修改:#{tp.self.name} by #{caller_gem}" end end.enable
这种方式更灵活,能精准区分合法与非法的类修改操作。
二、Ruby/Rails社区的现有应对方案
目前社区针对这类供应链攻击的防护主要围绕依赖验证和运行时监控两个方向:
- Bundler安全工具:
bundle audit可扫描Gem依赖中的已知漏洞,配合bundler-audit-checks扩展,能检测Gem中是否包含可疑的类重写代码;bundle verify则可以验证本地Gem的完整性,防止被篡改。 - Ruby 3.x安全增强:Ruby 3.0+引入了更严格的常量保护机制,比如
Module#const_get的安全选项,同时支持通过RubyVM::InstructionSequence做静态代码分析,提前发现可疑的类重写逻辑。 - Rails项目初始化防护:不少Rails项目会在初始化脚本(
config/initializers/freeze_core_classes.rb)中,先加载ActiveSupport,再冻结核心类和标准库类,只允许信任Gem修改类。 - Gem签名验证:使用
gem sign和gem verify可确保下载的Gem是开发者签名的未篡改版本,从源头减少供应链攻击风险。
三、处理信任库的例外情况
像ActiveSupport这类需要修改核心类的合法库,必须在冻结操作前完成加载:
- 在Gemfile中优先声明信任的核心Gem(activesupport、rails等)
- 执行
bundle install后,先加载这些Gem的代码 - 再执行冻结核心类或启动TracePoint监控的逻辑
- 在TracePoint的白名单中加入这些信任Gem的名称,避免误拦截
内容的提问来源于stack exchange,提问作者Anand
相关产品推荐
相关产品推荐

