我正在将对权限敏感的应用程序迁移到ActiveStorage。我需要确保只有具有权限的人才能访问文件,并且这些权限只能持续指定的时间段。
使用回形针,就像通过专家授权调用定义路线一样简单:
def thumbnail
authorize @record
redirect_to @record.thumbnail.expiring_url(300)
end
对/record/thumbnail
的任何调用都将由devise进行身份验证,由pundit授权,然后使用到期的URL重定向到S3。这是一个有效的过程。
移动到ActiveStorage时,我最初的想法是我将使用service_url
,但是它仅在使用S3时才有效-在测试中不起作用,在使用S3时不起作用在本地开发中使用磁盘服务。实际的“正确”方法是使用url_for
,或在上面的示例中redirect_to url_for @record.thumbnail
。
问题是,url_for
似乎发布了一个永久URL -它生成的URL对任何人而言始终是有效的,无需身份验证或授权。更重要的是,一旦有人拥有了该URL,我就无法撤消它,也不能说仅一周之久。有人拥有该URL后,他们将永远可以访问该文件。 (或者,至少在文件更新之前)。
我不认为这是一个巨大的安全漏洞,但绝对比使用Paperclip可以实现的落后了一步。我是否缺少一个重要的细节,或者这真的是ActiveStorage所能解决的吗?
答案 0 :(得分:2)
文档说:
<块引用>如果您需要在签名 blob 引用的隐匿性安全因素之外强制执行访问保护,则需要实现自己的经过身份验证的重定向控制器。
这不是特别有用。为此,您需要创建自己的控制器来为 blob 提供服务:
class BlobsController < ApplicationController
include ActiveStorage::SetBlob
before_action :authorize_blob
def show
expires_in ActiveStorage::Blob.service.url_expires_in
redirect_to @blob.service_url(disposition: params[:disposition])
end
private
def authorize_blob
# Your authorization code goes here
end
end
然后你需要设置你的路由:
get '/blobs/:signed_id/*filename' => "blobs#show", as: "service_blob"
direct :blob do |blob, options|
route_for(:service_blob, blob.signed_id, blob.filename, options)
end
resolve("ActiveStorage::Blob") { |blob, options| route_for(:blob, blob, options) }
最后,您需要禁用不安全的默认控制器,以便知识渊博的用户无法绕过您的授权。
如果您使用的是 Rails 6.1+,则可以通过在 application.rb 中将 config.active_storage.draw_routes
设置为 false 来实现。但是您需要确保其余的 ActiveStorage 路由是手动绘制的,并且其中有相当多的路由。你可以view them on Github。
否则,您将需要添加扩展控制器以重定向到您的新控制器(或只是将其完全关闭)。您可以将以下内容添加到初始值设定项:
module ActiveStorageRedirect
def self.included(controller)
controller.before_action :redirect_to_authenticated
end
private
def redirect_to_authenticated
redirect_to Rails.application.routes.url_helpers.blob_path(@blob)
end
end
ActiveStorage::Blobs::RedirectController.include(ActiveStorageRedirect) # For Rails >= 6.1
ActiveStorage::Blobs::ProxyController.include(ActiveStorageRedirect) # For Rails >= 6.1
# ActiveStorage::BlobsController.include(ActiveStorageRedirect) # For Rails < 6.1