ActiveStorage Direct Uploads Safe by Default/How to make it safe?

I’m looking to use ActiveStorage Direct Uploads in a project and I’m struggling to understand how the default configuration is safe.

As I read it, any user can create a new direct upload by calling DirectUploadController#create. They then receive a URL and/or authentication information for uploading the contents of the blob to your storage service. Couldn’t someone simply keep making calls to DirectUploadController#create and keep uploading more and more blobs to the storage service and never attaching any of those blobs?

Is that safe? Is there a risk of a user getting a super large bill from say S3 because a script kept sending blobs? Or maybe this is a similar level of risk as someone creating new database records on a rails site as well, I’m not sure.

Any thoughts on this? Am I looking at this problem totally wrong or is there something I’m not seeing?

1 Like

You’re not wrong. Active Storage’s built-in controllers are either unauthenticated or protected only by weak signature-based authentication:

  • ActiveStorage::BlobsController: Signature-based authentication, permanent signatures
  • ActiveStorage::RepresentationsController: Signature-based authentication, permanent signatures
  • ActiveStorage::DirectUploadsController: Unauthenticated (:warning:)
  • ActiveStorage::DiskController: Signature-based authentication, short-lived signatures

In a production app, you’ll likely need to authenticate, validate, rate-limit, and otherwise protect storage access more stringently. That means you’ll need to bring your own controllers.

We have warnings to this effect non-exhaustively peppered throughout the API documentation:

If you need to enforce access protection beyond the security-through-obscurity factor of the signed blob references, you’ll need to implement your own authenticated redirection controller.

We can more consistently/clearly/loudly document this—and PRs are welcome for that—but I don’t feel great about the current state of things regardless. We’re trading production safety for Fisher-Price simplicity. I don’t have a solution in mind at the moment.

/cc @bitsweat @DHH

2 Likes

Thanks for helping me understand it. I don’t think it’s helped by the fact that:

  • You can’t prevent drawing the default routes for ActiveStorage, until 6.1
  • There doesn’t seem to be a well documented way to override some of the default ActiveStorage routes.

For my use-case, I don’t really care that much about the authentication of the other controllers but I don’t like ActiveStorage::DirectUploadsController being unauthenticated.

I say all this without a good answer on how to fix the problem and still make it easy to get started AND not assume a particular authentication system or design.

1 Like

I was looking into finally implementing AS into one of our apps, decided to read a bit and get familiar with it and this topic blew my mind a bit.

Generally ActiveStorage engine works a lot like a closed black box in most cases. It is convenient and handles complex and cumbersome tasks beautifully but it lacks any documented customization options. And to be honest, lack of structured way to inject any form of authentication into controllers smells like a ticking bomb to me.

I can imagine that there is now a lot of production apps in the world with this controllers endpoints exposed without much concern. Why? Because this matter should be mentioned in huge black letters on top of the README and Guide pages. I know app developers should consider this when developing but people are lazy and I can bet my hair on the fact that huge set of devs assumed that implementing for example devise will cover AS controllers by default as well.

So my suggestion would be to add information about this to documentation ASAP util this matter is resolved in any way. I can open a PR later tomorrow for if you want.

Back to the matter at hand easiest option to solve this I can think of is to add the ability to easy register before_action to those controllers or customize the class ActiveStorage::BaseController inherits from and add authentication in this class.

Either way I strongly believe this should be documented in some way and AS documentation in general is not up to what this engine provides and can do for you now.

2 Likes