AWS S3 Secure Image Upload #611

Open
opened 2026-02-04 21:25:15 +03:00 by OVERLORD · 5 comments
Owner

Originally created by @svarlamov on GitHub (Mar 25, 2018).

For Feature Requests

Desired Feature: Secure image uploads (require auth) with S3 storage backend (the S3 version of #551 / #665)

Expected Behavior

Same user experience as local secure uploads, but via AWS S3 signed URLs. Also -- and I'm not sure how this would work now since the local secure feature has already been released -- but ideally it wouldn't use a 'new' storage method, but just add an 'authed images' flag. However, I'm sure that there's a good reason why it was implemented with a new storage method previously... Ideally, there would also be a streamlined way to migrate to secure images as well.

Originally created by @svarlamov on GitHub (Mar 25, 2018). ### For Feature Requests Desired Feature: Secure image uploads (require auth) with S3 storage backend (the S3 version of #551 / #665) ##### Expected Behavior Same user experience as local secure uploads, but via AWS S3 signed URLs. Also -- and I'm not sure how this would work now since the local secure feature has already been released -- but ideally it wouldn't use a 'new' storage method, but just add an 'authed images' flag. However, I'm sure that there's a good reason why it was implemented with a new storage method previously... Ideally, there would also be a streamlined way to migrate to secure images as well.
OVERLORD added the 🛠️ Enhancement Open to discussion labels 2026-02-04 21:25:15 +03:00
Author
Owner

@cb3inco commented on GitHub (Apr 1, 2021):

I'm not sure if I should create another feature request, but I too would like to see something like this build into Bookstack. Perhaps a more broader idea in the sense that it applies to maybe mounting a S3 bucket that can can operate outside of Bookstack to include media assets in bookstack. My use case is videos mainly. If uploading to S3 storage, it puts the video into a page as an attachment. But if I want to embed it from a bucket that is 'publicly' available a signed url would be much better than a direct url to prevent unauthorized access or a sharing of a URL bypassing Bookstack. S3 complaint storage supports this. Cloudflare supports this in their streaming service as well.

https://developers.cloudflare.com/stream/viewing-videos/securing-your-stream
https://docs.min.io/docs/upload-files-from-browser-using-pre-signed-urls.html

Both uploads and downloads for S3 type storage would be great. This likely would require handling these type of attachments differently than local storage. This would allow using a true cdn to serve content authenticated without needing to load it through bookstack attachment process. Context would be loaded for example via cdn.domain.com rather than bookstackdomain.com/attachment/3

@cb3inco commented on GitHub (Apr 1, 2021): I'm not sure if I should create another feature request, but I too would like to see something like this build into Bookstack. Perhaps a more broader idea in the sense that it applies to maybe mounting a S3 bucket that can can operate outside of Bookstack to include media assets in bookstack. My use case is videos mainly. If uploading to S3 storage, it puts the video into a page as an attachment. But if I want to embed it from a bucket that is 'publicly' available a signed url would be much better than a direct url to prevent unauthorized access or a sharing of a URL bypassing Bookstack. S3 complaint storage supports this. Cloudflare supports this in their streaming service as well. https://developers.cloudflare.com/stream/viewing-videos/securing-your-stream https://docs.min.io/docs/upload-files-from-browser-using-pre-signed-urls.html Both uploads and downloads for S3 type storage would be great. This likely would require handling these type of attachments differently than local storage. This would allow using a true cdn to serve content authenticated without needing to load it through bookstack attachment process. Context would be loaded for example via cdn.domain.com rather than bookstackdomain.com/attachment/3
Author
Owner

@muhzak commented on GitHub (Oct 20, 2023):

Does anyone have a workaround for this?

@muhzak commented on GitHub (Oct 20, 2023): Does anyone have a workaround for this?
Author
Owner

@zivillian commented on GitHub (Oct 23, 2024):

We implemented a logical theme for signed s3 image urls:

functions.php:

use BookStack\Facades\Theme;
use BookStack\Http\Controller;
use BookStack\Theming\ThemeEvents;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Filesystem\FilesystemManager;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Route;

class S3Controller extends Controller{
    
    public function __construct(
        protected FilesystemManager $fileSystem,
    ) {
    }

    public function signedRedirect($path){
        $disk = $this->fileSystem->disk('s3');
        $url = $disk->temporaryUrl($path, Carbon::now()->addMinutes(5));
        return redirect()->away($url);
    }
}

Theme::listen(ThemeEvents::ROUTES_REGISTER_WEB, function(){
    Route::middleware('auth')->group(function(){
        Route::get('/s3/{path}', [S3Controller::class, 'signedRedirect'])->where('path', '.*');
    });
});

.env:

STORAGE_TYPE=s3
STORAGE_S3_KEY=your-key
STORAGE_S3_SECRET=your-secret
STORAGE_S3_BUCKET=your-bucket
STORAGE_S3_REGION=your-region
STORAGE_S3_ENDPOINT=https://s3.your-region.your-provider.tld
STORAGE_URL=https://your.bookstack.domain.tld/s3/

The STORAGE_URL points to the S3Controller which is redirecting to the signed s3 url. The signed URLs are only valid for 5 minutes and the S3Controller itself requires a valid BookStack session.

@zivillian commented on GitHub (Oct 23, 2024): We implemented a logical theme for signed s3 image urls: `functions.php`: ```php use BookStack\Facades\Theme; use BookStack\Http\Controller; use BookStack\Theming\ThemeEvents; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Filesystem\FilesystemManager; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Route; class S3Controller extends Controller{ public function __construct( protected FilesystemManager $fileSystem, ) { } public function signedRedirect($path){ $disk = $this->fileSystem->disk('s3'); $url = $disk->temporaryUrl($path, Carbon::now()->addMinutes(5)); return redirect()->away($url); } } Theme::listen(ThemeEvents::ROUTES_REGISTER_WEB, function(){ Route::middleware('auth')->group(function(){ Route::get('/s3/{path}', [S3Controller::class, 'signedRedirect'])->where('path', '.*'); }); }); ``` `.env`: ```ini STORAGE_TYPE=s3 STORAGE_S3_KEY=your-key STORAGE_S3_SECRET=your-secret STORAGE_S3_BUCKET=your-bucket STORAGE_S3_REGION=your-region STORAGE_S3_ENDPOINT=https://s3.your-region.your-provider.tld STORAGE_URL=https://your.bookstack.domain.tld/s3/ ``` The `STORAGE_URL` points to the `S3Controller` which is redirecting to the signed s3 url. The signed URLs are only valid for 5 minutes and the S3Controller itself requires a valid BookStack session.
Author
Owner

@timnis commented on GitHub (Jun 11, 2025):

What I have tested looks like that Presigned URLs is also required with Garage .

I tested with following config:

STORAGE_TYPE=s3 STORAGE_S3_KEY=keyxxx STORAGE_S3_SECRET=secretxxx STORAGE_S3_BUCKET=bookstack-bucket STORAGE_S3_ENDPOINT=https://s3.domain.com STORAGE_S3_REGION=garage STORAGE_URL=https://s3.domain.com/bookstack-bucket

Image upload worked but Bookstack showed only links instead of image and when clicked link I got
Forbidden: Garage does not support anonymous access yet

@timnis commented on GitHub (Jun 11, 2025): What I have tested looks like that Presigned URLs is also required with [Garage ](https://garagehq.deuxfleurs.fr/). I tested with following config: ` STORAGE_TYPE=s3 STORAGE_S3_KEY=keyxxx STORAGE_S3_SECRET=secretxxx STORAGE_S3_BUCKET=bookstack-bucket STORAGE_S3_ENDPOINT=https://s3.domain.com STORAGE_S3_REGION=garage STORAGE_URL=https://s3.domain.com/bookstack-bucket ` Image upload worked but Bookstack showed only links instead of image and when clicked link I got `Forbidden: Garage does not support anonymous access yet`
Author
Owner

@ryanmortier commented on GitHub (Oct 14, 2025):

Would love to see this feature added please.

@ryanmortier commented on GitHub (Oct 14, 2025): Would love to see this feature added please.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/BookStack#611