我们使用refile允许用户将图像上传到我们的S3后端。此外,我们允许用户选择输入互联网上任何图像的URL(通过remote_image_url
属性)
只要输入的URL指向一个实际的文件,这就可以正常工作。但是,如果URL中有错误,或者提供了一些无意义的输入,Refile
将抛出以下异常:
Errno::ENOENT (No such file or directory @ rb_sysopen - thiswillnotwork):
app/controllers/my/deals_controller.rb:17:in `create'
appsignal (0.11.2) lib/appsignal/rack/listener.rb:13:in `call'
有没有一个选项可以忽略输入的URL无效的情况(类似于CarrierWave中的validate_download
选项),理想情况下,使用我们的回退图像?
我们已经尝试在raise_errors
选项设置为false
的情况下安装附件,但结果相同。
我们的项目使用Rails 4.2.0
和Refile 0.5.3
。
编辑:
我已经确认,此异常是来自Kernel.open
的较低级别SystemCallError
,并且Refile:不会挽救此异常类型
rescue OpenURI::HTTPError, RuntimeError => error
raise if error.is_a?(RuntimeError) and error.message !~ /redirection loop/
@errors = [:download_failed]
raise if @raise_errors
end
我正在处理一个拉取请求,以便重新提交以修复此问题。
编辑2:
在处理此问题时,我们在Refile
中发现了一个主要的安全问题,使潜在的攻击者能够使用远程代码执行。
Refile gem有一个功能,将提供URL并上传远程文件。这可以通过在表单中添加类似
remote_image_url
的字段来实现,其中image
是附件的名称。此功能使用开放uri发出此HTTP请求,而不验证传递的uri。攻击者可以在主机上创建一个执行任意shell命令的URI。
如果您使用的是Refile
版本0.5.0
-0.5.3
,请升级到最新版本。升级也将解决上述问题。
在与Refile
的维护人员交谈后,这将在下一次迭代中修复。目前,我们已经创建了以下非常简单的Ruby类作为解决方案。
# app/services/remote_url.rb
class RemoteUrl
def initialize(url)
@url = url
end
def valid?
URI.parse(@url).kind_of?(URI::HTTP)
rescue URI::InvalidURIError
false
end
end
我在我的控制器中使用它,就像这样:
# app/controllers/model_controller.rb
def model_params
sanitize_remote_url!
params.require(:model).permit(:description, ..., :image, :remote_image_url)
end
def sanitize_remote_url!
params[:model].delete(:remote_image_url) unless RemoteUrl.new(params[:model][:remote_image_url]).valid?
end
这是一个相当原始的方法,但考虑到它将在下一个Gem版本中进行修补,我认为目前这已经足够好了。
免责声明:
尽管这抵消了原始问题的edit2中提到的一些漏洞,但如果攻击者能够在文件系统上创建名为http:
的文件夹,则攻击者仍然能够执行远程代码,因为像http:/../etc/passwd
这样的字符串将通过URL验证,但仍能通过Kernel.open
读取本地文件。
Gem现在已经打了补丁(0.5.4
),升级是正确的解决方案。