Назад | Перейти на главную страницу

Безопасная ссылка зависит только от имени каталога?

В настоящее время я изо всех сил пытаюсь сделать nginx безопасным только для имени папки, независимо от имени файла внутри нее. Скажем, я обращаюсь к файлу в папке / one / two / three, это будет выглядеть так:

http://example.com/one/two/35d6d33467aae9a2e3dccb4b6b027878/file.mp3

Таким образом, к папке «три» будет доступ только из каталога md5, а реальный путь вернет 403. У меня есть тысячи таких папок, поэтому мне нужно держать их скрытыми, но иметь статический доступ к ним через удаленных клиентов, которые знают только md5 в время выполнения.

Между тем, такие ссылки тоже должны работать:

http://example.com/one/two/35d6d33467aae9a2e3dccb4b6b027878/four/file.mp3

Таким образом, скрыт только определенный уровень каталога.

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

Прямой доступ к вашим скрытым файлам (например, /protected/folder1/folder2/file.pdf) не разрешен Nginx, поскольку это местоположение было помечено как внутреннее. Но ваш скрипт может перенаправить в это место со специальным заголовком X-Accel-Redirect.

Таким образом, вы можете позволить Nginx делать то, что он умеет лучше всего, доставлять данные, а ваш скрипт проверяет, только если доступ разрешен или нет.

Ниже вы можете увидеть простой пример этого.

Папка / данные содержат общедоступный контент (например, общедоступные изображения). Непубличные изображения хранились в другой папке (вне htdocs) и предоставлялись через location / protected_data. Это место имеет псевдоним для папки, содержащей защищенные изображения, и директиву internal. Так что это недоступно извне.

В сценарии PHP я сначала проверил, существует ли защищенный файл. Это может быть проблемой безопасности, но обычно проверка прав пользователя обходится дороже (отнимает много времени), чем простой file_exists. Поэтому, если безопасность важнее производительности, вы можете изменить порядок проверок.

Конфигурация сервера Nginx:

...

root /var/www/test/htdocs;

location / {
    index index.php index.htm index.html;
}

location /data {
    expires 30d;
    try_files $uri /grant-access.php;
}

location /protected_data {
    expires off;
    internal;
    alias /var/www/test/protected_data;
}

location ~ \.php$ {
    if (!-e $request_filename) {
        rewrite     /       /index.php last;
    }
    expires                 off;
    include                 fastcgi_params;
    fastcgi_pass            unix:/var/run/php5-fpm.sock;
    fastcgi_read_timeout    300;
    fastcgi_param           SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    access_log              /var/log/nginx/access.log combined;
}
...

Скрипт PHP:

<?php
// this is the folder where protected files are stored (see Nginx config alias directive of the internal location)
define('PROTECTED_FOLDER_FILESYSTEM', '/var/www/test/protected_data');

// this is the url path we have to replace (see Nginx config with the try_files directive)
define('PROTECTED_PUBLIC_URL', '/data');

// this is the url path replacement (see Nginx config with the internal directive)
define('PROTECTED_INTERNAL_URL', '/protected_data');

// check if file exists
$filename = str_replace(
    PROTECTED_PUBLIC_URL .'/',
    '/',
    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);
if (!file_exists(PROTECTED_FOLDER_FILESYSTEM . $filename)) {
    http_response_code(404);
    exit;
}

// check if access is allowed (here we will use a random check)
if (rand(1,2)==1) {
    // grant access
    header('X-Accel-Redirect: ' . PROTECTED_INTERNAL_URL . $filename);
} else {
    // deny access
    http_response_code(403);
}

Чтобы это стало возможным, nginx должен иметь возможность определять, что хэш 35d6d33467aae9a2e3dccb4b6b027878 соответствует three. На сегодняшний день Nginx не может этого сделать (и я не думаю, что это входит в список задач).

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

Например, местоположение вашего веб-сервера http://example.com/one/two/ указывает на каталог (скажем /var/www/html/), где символическая ссылка 35d6d33467aae9a2e3dccb4b6b027878 указывает на каталог three/ который находится в другом месте (например, /var/www/protected/).

При загрузке потребуется запустить скрипт или что-то подобное, чтобы создать папку. three/ в /var/www/protected/, хеш "три", а затем создать символическую ссылку /var/www/html/35d6d33467aae9a2e3dccb4b6b027878.

Это единственный способ, который я мог придумать.