如何向 ActiveStorage 添加权限?

Ron*_*gge 3 rails-activestorage

我正在将一个对权限敏感的应用程序迁移到 ActiveStorage。我需要确保文件只能由具有权限的人访问,并且这些权限仅持续指定的时间段。

使用 Paperclip,这就像使用专家授权调用定义路线一样简单:

def thumbnail
    authorize @record
    redirect_to @record.thumbnail.expiring_url(300)
end
Run Code Online (Sandbox Code Playgroud)

任何调用都/record/thumbnail将由 devise 进行身份验证,由 pundit 授权,然后使用过期的 URL 重定向到 S3。这是一个有效的过程。

转向 ActiveStorage,我最初的想法是我会使用service_url,但它仅在我使用 S3 时才有效 - 它在测试中不起作用,如果我在使用磁盘服务进行本地开发时它不起作用。实际的“正确”方法是使用url_for, 或 在上面的示例中redirect_to url_for @record.thumbnail

问题是,url_for似乎发出了一个永久 url——它生成的 url 对任何人来说始终有效,无需身份验证或授权。更重要的是,一旦有人拥有了那个网址,我就无法撤销它,我不能说它只有效一周。一旦某人拥有该 URL,他们就可以永远访问该文件。(或者,大概,至少在文件更新之前)。

我不认为这是一个巨大的安全缺陷,但这绝对是使用 Paperclip 所能实现的目标的倒退。我是否遗漏了一个重要的细节,或者这真的是 ActiveStorage 所能做到的一切吗?

Mik*_*rer 8

文档说:

如果您需要在签名 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
Run Code Online (Sandbox Code Playgroud)

然后你需要设置你的路由:

  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) }  
Run Code Online (Sandbox Code Playgroud)

最后,您需要禁用不安全的默认控制器,以便知识丰富的用户无法规避您的授权。

如果您使用的是 Rails 6.1+,则可以通过config.active_storage.draw_routes在 application.rb 中设置为 false 来完成此操作。但随后您需要确保其余的 ActiveStorage 路由是手动绘制的,并且其中有相当多的路由。您可以在 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

Run Code Online (Sandbox Code Playgroud)