Перейти к основному содержимому

Ограничение доступа к публичным ассетам

Pimcore имеет следующее стандартное поведение в отношении доставки ассетов:

Все данные, которые сохраняются в качестве ассетов Pimcore, доступны по их URL (например, https://mydomain.com/my-assetfolder/my-asset.jpg), и, следовательно, являются публичными, без необходимости входа в систему или других ограничений доступа!

В связи с этим конфиденциальные данные не должны храниться в виде ассетов Pimcore без дополнительных мер защиты.

Причина такого поведения связана с производительностью. Доставка ассета напрямую через веб-сервер требует значительно меньше ресурсов, чем запуск процесса PHP для каждого запроса ассета (особенно при доставке миниатюр).

Если требуется более строгое ограничение доступа, Pimcore предоставляет два варианта решения этой задачи:

Вариант 1 - Полное ограничение доступа к определенным ассетам

Все конфиденциальные активы необходимо хранить в одной (или нескольких) папках, например, в /protected (чтобы задать права доступа в бэкенде Pimcore, это необходимо сделать в любом случае).

Защищенная папка

Apache

В .htaccess проекта доступ к этой папке необходимо ограничить с помощью дополнительного правила перезаписи. Важно, чтобы это правило было помещено перед правилом перезаписи для доставки ассетов.

...  
RewriteRule ^protected/.* - [F,L]
RewriteRule ^var/.*/protected(.*) - [F,L]
RewriteRule ^cache-buster\-[\d]+/protected(.*) - [F,L]

# АССЕТЫ: проверьте, используется ли метод запроса GET (из-за WebDAV) и существует ли запрошенный файл (ассет) в файловой системе, если оба параметра совпадают, доставьте ассет напрямую
...

Nginx

Добавьте следующие части в вашу конфигурацию Nginx сразу после директивы index.

location ~ ^/protected/.* {  
return 403;
}

location ~ ^/var/.*/protected(.*) {
return 403;
}

location ~ ^/cache-buster\-[\d]+/protected(.*) {
return 403;
}

Полный пример конфигурации можно найти на этой странице.

Из-за этого правила все ассеты, находящиеся в /protected (включая все их миниатюры), больше не доставляются через веб-сервер. Следовательно, также не могут быть использованы прямые ссылки для загрузки или теги img, сгенерированные Pimcore для миниатюр. Все поставки этих ассетов должны выполняться вручную через пользовательский экшен контроллера.

Вариант 2 - Проверка прав доступа перед доставкой

Этот вариант не ограничивает доставку в целом, а перенаправляет запрос ассета к экшену контроллера, который может проверить права доступа с использованием бизнес-логики и затем доставить ассет или нет.

Опять же, все конфиденциальные ассеты необходимо хранить в одной (или нескольких) папках, например, в /protected.

Защищенная папка

Apache

В .htaccess проекта запросы к ассетам из этой папки должны быть перенаправлены на index.php. Важно, чтобы это правило было помещено перед правилом перезаписи для доставки ассетов.

...  
RewriteRule ^protected/(.*) %{ENV:BASE}/index.php [L]
RewriteRule ^var/.*/protected(.*) - [F,L]
RewriteRule ^cache-buster\-[\d]+/protected(.*) - [F,L]

# АССЕТЫ: проверьте, используется ли метод запроса GET (из-за WebDAV) и существует ли запрошенный файл (ассет) в файловой системе, если оба параметра совпадают, доставьте ассет напрямую
...

Nginx

Добавьте следующие части в вашу конфигурацию Nginx сразу после директивы index.

rewrite ^(/protected/.*) /index.php$is_args$args last;  

location ~ ^/var/.*/protected(.*) {
return 403;
}

rewrite ^(/cache-buster-(?:\d+)/protected(?:.*)) /index.php$is_args$args last;

location ~ ^/cache-buster\-[\d]+/protected(.*) {
return 403;
}

Полный пример конфигурации можно найти на этой странице.

В приложении должен быть определён маршрут (в config/routes.yaml) и экшен контроллера который обрабатывает запрос, например, следующим образом:

# config/routes.yaml  

# важно, это должны быть верхние маршруты в файле!
asset_protect:
path: /protected/{path}
defaults: { _controller: App\Controller\MyAssetController::protectedAssetAction }
requirements:
path: '.*'

cache_buster_asset_protect:
path: /cache-buster-{id}/protected/{path}
defaults: { _controller: App\Controller\MyAssetController:protectedAssetAction }
requirements:
id: '\d+'
path: '.*'

<?php  

namespace App\Controller;

use Pimcore\Controller\FrontendController;
use Pimcore\Model\Asset;
use Pimcore\Model\Asset\Service;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class MyAssetController extends FrontendController
{
public function protectedAssetAction(Request $request): Response
{
// ВАЖНО!
// Добавьте свой код здесь, чтобы проверить разрешение!


// the following code is responsible to deliver asset & thumbnail contents
// modify it the way you need it for your use-case
$pathInfo = $request->getPathInfo();
$asset = Asset::getByPath($pathInfo);
if ($asset){
$stream = $asset->getStream();
return new StreamedResponse(function () use ($stream) {
fpassthru($stream);
}, 200, [
'Content-Type' => $asset->getMimeType(),
]);
} else {
return Asset\Service::getStreamedResponseByUri($pathInfo);
}

throw new AccessDeniedHttpException('Access denied.');
}
}

Конечно, этот параметр существенно влияет на загрузку сервера и производительность доставки ассетов (и миниатюр). Поэтому рекомендуется доставлять таким образом не все ассеты, а только конфиденциальные!


Вы можете предложить улучшение документации или задать вопрос в комментариях.
Если вам нужна полноценная консультация — вы можете заказать её на нашем сайте.