Ruby on Rails 6.1中从数据库获取Active Storage变体选项的最佳方案及安全疑问
替代
eval的安全方案与$SAFE使用建议 你说得太对了——在用户可控的输入上使用eval是极度危险的操作,哪怕加上$SAFE也不是稳妥的解决方案。下面给你几个更安全、更符合Rails最佳实践的替代方案,以及关于$SAFE的明确建议:
方案1:使用Rails序列化哈希(首推)
Rails原生支持将哈希序列化存储到数据库的text字段中,完全不需要手动解析或执行代码,既安全又省心:
1. 更新Slideshow模型
# app/models/slideshow.rb class Slideshow < ApplicationRecord # 自动将options字段序列化为Hash,读取时直接返回哈希对象 serialize :options, Hash # 设置默认值,避免nil导致的错误 def options super || { resize_to_limit: [300, 222], kuwahara: '3%' } end end
2. 优化Admin表单
把用户输入改成JSON格式(比Ruby哈希更通用且易解析),避免用户输入Ruby语法带来的风险:
# app/admin/slideshow.rb form do |f| f.inputs 'Slideshow' do f.input :name f.input :options, as: :text, input_html: { value: f.object.options.to_json, rows: 3 }, label: '配置选项(JSON格式)示例:{"resize_to_limit": [300, 222], "monochrome": true}' f.input :images, as: :file, input_html: { multiple: true } end f.actions end
3. 简化控制器代码
直接读取模型返回的哈希即可,彻底告别eval:
# app/controllers/slideshow_controllers.rb def options @options = slideshow&.options || { resize_to_limit: [300, 222], kuwahara: '3%' } end def slideshow Slideshow.published.take end
这样用户输入的JSON会被自动转换成Ruby哈希,Active Storage的variant方法可以直接使用这个哈希参数,完全没有代码注入的风险。
方案2:白名单验证(适合复杂场景)
如果业务需要更灵活的配置,可以提前定义允许的处理器和参数,只解析白名单内的内容。比如:
# 示例:只允许指定的图像处理方法 ALLOWED_TRANSFORMS = %i[resize_to_limit kuwahara monochrome].freeze def parse_options(raw_options) # 先解析成哈希 parsed = JSON.parse(raw_options) # 只保留白名单内的键,转换为符号键 parsed.slice(*ALLOWED_TRANSFORMS.map(&:to_s)).transform_keys(&:to_sym) rescue JSON::ParserError # 解析失败返回默认配置 { resize_to_limit: [300, 222], kuwahara: '3%' } end
但这个方案比序列化哈希复杂,除非有特殊需求,否则首推方案1。
关于$SAFE的明确建议
绝对不建议使用$SAFE:这个特性在Ruby 2.7版本已经被正式废弃,而且它的安全模型设计老旧,无法抵御现代的代码注入攻击。即使设置了$SAFE = 1,也不能完全阻止恶意代码执行,反而会让代码变得晦涩且难以维护。
正确的安全思路是从根源上避免执行用户输入的代码,用序列化、JSON解析或白名单验证的方式处理配置,彻底消除代码注入的风险。
内容的提问来源于stack exchange,提问作者Alex




