How secure are your uploaded files? During SilverStripe 4.0 development, we thought about how folks were uploading and sharing files through their SilverStripe applications.
You see, unlike Pages (and even some DataObjects), files aren't versioned in SilverStripe 3.x. That means when you upload your files to the server, they're publicly accessible. Someone needs to guess a valid URL to see them, but that's not a bulletproof solution.
You can find the code for this post at https://github.com/assertchris-tutorials/tutorial-silverstripe-secure-assets.
There's a module for that!
Fortunately, there's a module to solve this problem. It's called silverstripe/secureassets, and it's relatively straightforward to set up.
Open a terminal window up to your application code folder, and try the following command:
$ composer require silverstripe/secureassets
After you run dev/build, you'll start to see permission settings on folders:
A good default is to upload files to a "secure" folder by default. Then you can move them to an "unsecure" folder when you're ready to show them to the world.
Imagine you wanted to create a new Page type (let's call it ScreencastPage). And you wanted to upload video files to that page. You might start with something like:
class ScreencastPage extends Page { private static $has_many = [ "VideoFiles" => "File", ]; public function getCMSFields() { $fields = parent::getCMSFields(); // Add file upload to its own tab $upload = new UploadField("VideoFiles"); $upload->setAllowedFileCategories("mov"); $upload->setFolderName("secure"); $fields->insertAfter(new Tab("VideoFiles"), "Main"); $fields->addFieldToTab("Root.VideoFiles", $upload); return $fields; } }
This will create a new Tab, with an UploadField attached. Files uploaded through this field will be stored in the secure folder, which you can protect against requests from unauthorised users.
For this to work, you also need to create a File extension, and apply it to files:
class VideoFileExtension extends DataExtension { /** * @var array */ private static $has_one = [ "ScreencastPage" => "ScreencastPage", ]; }
File: extensions: - VideoFileExtension
But what about when you want those video files to be visible to the general public. Well, perhaps you'd like them to be moved to a public (or unsecure) folder once the page they're attached to is published?
public function onAfterWrite() { parent::onAfterWrite(); /** @var Versioned $versioned */ $versioned = $this; // if this screencast page is published... if ($versioned->latestPublished()) { foreach ($this->VideoFiles() as $file) { /** @var Folder $folder */ $folder = Folder::find_or_make("unsecure"); if ($folder) { $file->setParentID($folder->ID); $file->write(); } } } }
With an onAfterWrite hook, we can check if the ScreencastPage is published. Then we can move each file from the secure to the unsecure folder.
Remember to call $file->write() or the filesystem won't reflect the database change...
The silverstripe/secureassets module has docs and tests, so you're bound to have a good experience with it. It's also part of the Common Web Platform basic recipe, so there's a good chance you already have it installed and ready to go!
Image by violscraper.
Post your comment
Comments
Posted by Gerardo Follie, 09/08/2016 2:50pm (8 years ago)
Posted by Jan, 04/08/2016 5:58pm (8 years ago)
Posted by David Alexander, 13/07/2016 10:30am (8 years ago)
Posted by neil, 07/07/2016 10:45pm (8 years ago)
Posted by Sander, 07/07/2016 10:35pm (8 years ago)
No one has commented on this page yet.
RSS feed for comments on this page | RSS feed for all comments