Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

 

Goldilocks and the unsecured assets

There's a module that helps you increase the security of your files. Let's take a look!

Read post

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:

permissions

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...

move

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.

About the author
Christopher Pitt

Chris works in the bespoke team, at SilverStripe. He helps build and maintain many of the modules used in SilverStripe applications. He also teaches; speaking at local user groups and international conferences, and through technical blog posts and books.

He also enjoys building robots and connecting those to circuits inside Minecraft. When the machines take over, he hopes this will buy him some time to formulate a plan of escape.