How to ensure ActiveStorage URL actually expires?

Viewed 972

EDIT: Discussion is continued in the Rails discussion group.

For now this monkey-patch did the trick (apply it with ActiveStorage::Blob.include(ActiveStorageBlobMonkeyPatch)):

module ActiveStorageBlobMonkeyPatch
  extend ActiveSupport::Concern

  included do
    def signed_id
      ActiveStorage.verifier.generate(id, purpose: :blob_id, expires_in: 5.minutes)
    end
  end
end

Original question:

If I load a page containing a URL to an ActiveStorage attachment (*), wait over 5 minutes, copy the URL from the source code and paste it in a browser, it loads fine although I would expect it to fail as the expiry time has been exceeded. This seems to be because the base64-encoded part of the URL contains "exp"=>nil:

url = app.url_for(post.cover_image)
params = url[/blobs\/(.*)--/,1]
JSON.parse(Base64.decode64(params))
=> {"_rails"=>{"message"=>"BAhpJA==", "exp"=>nil, "pur"=>"blob_id"}}

Only after querying that URL does it perform a 302 redirect which leads to a new URL which itself does contains an expiry of 5 minutes in the future.

How to protect images and other types of sensitive content (e.g. subscription-only) with ActiveStorage? How to ensure URLs for attachments and their variants will not be downloadable in the future?

(*) Tested with a Rails 6.0.2.2 app with Posts configured with has_one_attached :cover_image and using ActiveStorage disk service. config.active_storage.service_urls_expire_in was not defined and so defaults to 5 minutes. Posts were rendered with style="background-image: url(#{url_for post.cover_image)})" (not using variants for the sake of simplicity). The URL of the background image in the page source has the structure /rails/active_storage/blobs/XXX--YYY/ZZZ.jpg where XXX is the Base64-encoded part.

0 Answers
Related