PHPackages                             wwwision/privateresources - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. wwwision/privateresources

ActiveNeos-package[Security](/categories/security)

wwwision/privateresources
=========================

A Flow package that allows for protecting persistent resources from unauthorized access

v6.3.0(2y ago)1136.9k↓37.1%102MITPHP

Since Nov 14Pushed 2y ago4 watchersCompare

[ Source](https://github.com/bwaidelich/Wwwision.PrivateResources)[ Packagist](https://packagist.org/packages/wwwision/privateresources)[ Fund](https://www.paypal.me/bwaidelich)[ GitHub Sponsors](https://github.com/bwaidelich)[ RSS](/packages/wwwision-privateresources/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (2)Versions (21)Used By (2)

Wwwision.PrivateResources
=========================

[](#wwwisionprivateresources)

This is a Flow package that allows for protecting persistent resources from unauthorized access.

By default Flow publishes persistent resources to the `_Resources/Persistent` folder inside the web root making them accessible to browsers and other clients (unless using a different `PublishingTarget`, see below). That means that even files served from a protected area of your web application will be accessible for anyone knowing the internal filename of that resource. This is not a big deal usually, because the filename is determined by a hash over the actual file *contents* - so if you know the hash, you most probably know the file content anyways. But in some cases you need more control over the served files or want to prevent direct links to files being shared. In these cases this package might be of help:

It provides a new *Resource Publishing Target*, named `ProtectedResourceTarget` that, in contrast to other targets, won't copy file contents to a public directory (or CDN) upon publishing. Instead it will return a signed URL that will only work for users with *the same privileges as the current user*. That means, if an image is rendered to an authenticated user, the image URL will only resolve for users with *the same roles*, for other users it will return an HTTP status of `403 Forbidden`.

Disclaimer:
-----------

[](#disclaimer)

With this package user's can't easily share URLs to protected resources as they will only work for users with the same roles. However, resources will still be downloaded obviously and users can share them otherwise. Furthermore serving private resources consumes more time and memory because every hit triggers a PHP request. Conclusion: This package is only useful in very rare cases ;)

Version:
--------

[](#version)

The table below provides an overview of the available versions.

Note: The releases version 3.4.0 is not compatible with Flow &lt; 6.

Branch / ReleaseSupported Flow version3.x, 4.x4.1, 5.x3.4, 5.x6.x6.x, master7.x, 8.xHow-To:
-------

[](#how-to)

- Install the package to `Packages/Application/Wwwision.PrivateResources` (e.g. via `composer require wwwision/privateresources`)
- Configure it (see below)
- Done

Concept:
--------

[](#concept)

This package provides a custom [Resource Publishing Target](https://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/ResourceManagement.html#target)that prevents resources from being published to the accessible file system. Instead it generates a token link that, when requested, invokes a [HTTP Middleware](https://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/Http.html#middlewares-chain) that in turn serves the corresponding resource if the current user is allowed to access it.

Configuration:
--------------

[](#configuration)

### Publishing Target

[](#publishing-target)

First of all, you'll have to activate the `ProtectedResourceTarget` in your `Settings.yaml`. You can either create a new *resource collection*:

```
Neos:
  Flow:
    resource:
      collections:
        'protectedResources':
          storage: 'defaultPersistentResourcesStorage'
          target: 'protectedResourcesTarget'
```

You then can use this feature by uploading resources to the "protectedResources" collection, e.g. with help of Fluid and the `UploadViewHelper`:

```

```

If you want to enable this feature *globally* for persistent resources, just override the target of the existing "persistent" collection:

```
Neos:
  Flow:
    resource:
      collections:
        'persistent':
          target: 'protectedResourcesTarget'
```

**NOTE:** Serving protected resources will have a negative effect on performance and memory consumption - only activate this globally, if you really want to protect *all* persistent resources.

#### Token Lifetime

[](#token-lifetime)

By default a token never expires. You can change that with the `tokenLifetime` option:

```
Neos:
  Flow:
    resource:
      targets:
        'protectedResourcesTarget':
          targetOptions:
            tokenLifetime: 86400
```

With this configuration, tokens will expire after 86400 seconds (= one day).

**NOTE:** This option is only considered for "whitelisted" tokens that are not bound to a role or security context hash

**NOTE:** If the publishing of the resource is cached, this might lead to broken resources (e.g. if you use this within Neos CMS within a Node Type with a cache lifetime larger than the tokenLifetime).

#### Whitelist Roles

[](#whitelist-roles)

Sometimes you want to prevent resources from being protected if a certain role is authenticated. With the `whitelistRoles`option you can disable the token enforcement for individual roles (The token will still be generated but it won't be verified any longer if one of the specified roles is authenticated):

```
Neos:
  Flow:
    resource:
      targets:
        'protectedResourcesTarget':
          targetOptions:
            whitelistRoles: ['Your.Package:SomeRole']
```

By default, the `Neos.Neos:Editor` role is whitelisted, so that this package can be used within Neos CMS without caching issues.

#### Privileged Role

[](#privileged-role)

If no whitelisted role is active (see above) the token is bound to the Flow Security Context. That means that only requests that have the same Security Context Hash are privileged to access the resource. The Security Context Hash is bound to the currently authenticated roles and the [Global AOP Objects](https://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/Security.html#content-security-entityprivilege).

Alternatively a role can be configured that should always have access to all resources of a given Resource Target:

```
Neos:
  Flow:
    resource:
      targets:
        'protectedResourcesTarget':
          targetOptions:
            privilegedRole: 'Your.Package:SomeRole'
```

**NOTE:** With this option it's allowed to create the resource URLs asynchronous and/or via CLI because it doesn't require an initialized Security Context

### HTTP Middleware

[](#http-middleware)

The actual serving of protected files is done using a `HTTP Middleware` that will be triggered even before the regular routing kicks in. This `ProtectedResourceMiddleware` is already configured and if it comes across an HTTP requests with an "\_\_protectedResource" argument it will validate the hash and output the requested file, if valid.

By default it uses PHPs `readfile()` function to stream a local file from its inaccessible location to the client, but this has some drawbacks because it has to pipe the whole file through the PHP process consuming a lot of memory, especially for larger files.

To support external cloud storage use the `StreamStrategy` described further below.

To improve performance and memory footprint you can therefore configure the middleware to use different strategies to serve the file:

#### X-Sendfile (Apache)

[](#x-sendfile-apache)

mod\_xsendfile is a small **Apache2** module that processes X-SENDFILE headers registered by the original output handler (see [https://tn123.org/mod\_xsendfile/](https://tn123.org/mod_xsendfile/)).

Assuming you have the Apache module installed and configured to access files within the `Data/Persistent/Resources`directory of your installation, you can activate the `XSendfileStrategy` with the following settings:

```
Wwwision:
  PrivateResources:
    middleware:
      serveStrategy: 'Wwwision\PrivateResources\Http\FileServeStrategy\XSendfileStrategy'
```

Instead of using `readfile()` to serve the file, the HTTP Middleware will then send an `X-Sendfile` header pointing to the internal file, letting Apache take care of the download.

#### X-Accel-Redirect (Nginx)

[](#x-accel-redirect-nginx)

Similar to the `X-Sendfile` mechanism, the `X-Accel-Redirect` allows for internal redirection to a location in **Nginx** environments (see ).

It can be activated with:

```
Wwwision:
  PrivateResources:
    middleware:
      serveStrategy: 'Wwwision\PrivateResources\Http\FileServeStrategy\XAccelRedirectStrategy'
```

#### Stream

[](#stream)

This strategy uses the read-only stream offered by the ResourceManager. It should be compatible with all StorageInterfaces and supports external storage (e.g. AWS S3, Google Cloud Storage GCS). But this has some drawbacks because it has to pipe the whole file through the PHP process consuming a lot of memory and CPU time.

It can be activated with:

```
Wwwision:
  PrivateResources:
    middleware:
      serveStrategy: 'Wwwision\PrivateResources\Http\FileServeStrategy\StreamStrategy'
```

#### Custom strategy

[](#custom-strategy)

You can create your own strategy for serving files, implementing the `FileServeStrategyInterface`. With this you could for example realize protected CDN resources.

Signals
-------

[](#signals)

The HTTP Middleware triggers a signal whenever a protected resource is being accessed (see Flow documentation regarding more details about **Signals and Slots**). You can use that signal to count file downloads for example:

```
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\ResourceManagement\PersistentResource;
use Psr\Http\Message\ServerRequestInterface as HttpRequestInterface;
use Wwwision\PrivateResources\Http\Middleware\ProtectedResourceMiddleware;

final class Package extends \Neos\Flow\Package
{
    public function boot(Bootstrap $bootstrap): void
    {
        $dispatcher = $bootstrap->getSignalSlotDispatcher();
        $dispatcher->connect(ProtectedResourceMiddleware::class, 'resourceServed', function (PersistentResource $resource, HttpRequestInterface $httpRequest) {
            // increase counter for the given $resource
        });
    }
}
```

The following signals are emitted:

- `ProtectedResourceMiddleware:resourceServed(PersistentResource $resource, HttpRequest $httpRequest)` whenever a private resource is served
- `ProtectedResourceMiddleware:accessDenied(array $tokenData, HttpRequest $httpRequest)`

The following signal is still emitted for backwards compatibility, but is deprecated in favor of `accessDenied`:

- `ProtectedResourceMiddleware:invalidSecurityContextHash(array $tokenData, HttpRequest $httpRequest)`

Neos CMS
--------

[](#neos-cms)

This package works well with [Neos CMS](https://www.neos.io), but Neos currently doesn't offer a way to select a *resource collection*when uploading files or working with the Media Module. You can, however, activate protected resources globally (see above) or create custom editors for your protected file uploads.

### Authentication

[](#authentication)

In order to limit access to *Neos Backend users* the [Request Patterns](https://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/Security.html#request-patterns)have to match the `/__protectedResource?token=` request in order for the Neos authentication to be active. Starting with version 6.2 the `controllerObjectName` option can be used to simulate a Neos controller, for example:

```
Neos:
  Flow:
    resource:
      targets:
        'protectedResourcesTarget':
          targetOptions:
            # Limit access to Neos editors
            privilegedRole: 'Neos.Neos:Editor'

Wwwision:
  PrivateResources:
    middleware:
      # Simulate the NodeController to be invoked such that the Neos authentication gets activated
      controllerObjectName: 'Neos\Neos\Controller\Frontend\NodeController'
```

*Note:* If there are more request pattern types configured, those might have to be adjusted/removed

Known issues and limitations
----------------------------

[](#known-issues-and-limitations)

- Private resources currently only work for **persistent** resources. **Static** resources are not yet covered

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity38

Limited adoption so far

Community24

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~138 days

Recently: every ~250 days

Total

20

Last Release

852d ago

Major Versions

v2.0.0 → v3.0.02017-09-14

v1.1.0 → 3.4.x-dev2020-01-24

v3.4.0 → 4.0.x-dev2020-06-30

v4.0.0 → v5.0.02020-06-30

v5.0.0 → v6.0.02021-04-19

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/307571?v=4)[Bastian Waidelich](/maintainers/bwaidelich)[@bwaidelich](https://github.com/bwaidelich)

---

Top Contributors

[![bwaidelich](https://avatars.githubusercontent.com/u/307571?v=4)](https://github.com/bwaidelich "bwaidelich (20 commits)")[![andrehoffmann30](https://avatars.githubusercontent.com/u/23524251?v=4)](https://github.com/andrehoffmann30 "andrehoffmann30 (12 commits)")[![jrenggli](https://avatars.githubusercontent.com/u/743094?v=4)](https://github.com/jrenggli "jrenggli (4 commits)")[![lorenzulrich](https://avatars.githubusercontent.com/u/1816023?v=4)](https://github.com/lorenzulrich "lorenzulrich (3 commits)")[![robertlemke](https://avatars.githubusercontent.com/u/95582?v=4)](https://github.com/robertlemke "robertlemke (3 commits)")[![vertexvaar](https://avatars.githubusercontent.com/u/5594393?v=4)](https://github.com/vertexvaar "vertexvaar (2 commits)")[![daniellienert](https://avatars.githubusercontent.com/u/642226?v=4)](https://github.com/daniellienert "daniellienert (2 commits)")[![haase-fabian](https://avatars.githubusercontent.com/u/55744962?v=4)](https://github.com/haase-fabian "haase-fabian (1 commits)")[![fnkr](https://avatars.githubusercontent.com/u/616991?v=4)](https://github.com/fnkr "fnkr (1 commits)")

### Embed Badge

![Health badge](/badges/wwwision-privateresources/health.svg)

```
[![Health](https://phpackages.com/badges/wwwision-privateresources/health.svg)](https://phpackages.com/packages/wwwision-privateresources)
```

###  Alternatives

[mews/purifier

Laravel 5/6/7/8/9/10 HtmlPurifier Package

2.0k16.7M113](/packages/mews-purifier)[acmephp/core

Raw implementation of the ACME protocol in PHP

38973.7k7](/packages/acmephp-core)[mittwald/vault-php

A PHP client library for 'Vault by HashiCorp'

5392.1k](/packages/mittwald-vault-php)[stevenmaguire/laravel-middleware-csp

Provides support for enforcing Content Security Policy with headers in Laravel responses.

39107.6k](/packages/stevenmaguire-laravel-middleware-csp)[php-heroku-client/php-heroku-client

A PHP client for the Heroku Platform API

24404.8k4](/packages/php-heroku-client-php-heroku-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
