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.

Post your comment

Comments

  • This setup allows you to forgo mounting the cloud storage via ss, CloudFuse, etc but shouldn't require changes to the Silverstripe file subsystem.

    Posted by Gerardo Follie, 09/08/2016 2:50pm (8 years ago)

  • Very useful module. Is there any hack to run it with nginx?

    Posted by Jan, 04/08/2016 5:58pm (8 years ago)

  • Short, sweet, useful. Keep 'em coming Christopher :)

    Posted by David Alexander, 13/07/2016 10:30am (8 years ago)

  • This is the sort of article I really enjoy seeing here – useful, informative, not-too-intimidating-to-beginners. Good work!

    Posted by neil, 07/07/2016 10:45pm (8 years ago)

  • Thanks for the article. It's an interesting solution and I'm trying to understand its benefits over something more traditional like storing the files outside the web root and just using controller logic to determine whether file access is allowed and returning the file through PHP. Perhaps someone could elaborate?

    Posted by Sander, 07/07/2016 10:35pm (8 years ago)

RSS feed for comments on this page | RSS feed for all comments