安全链接仅取决于目录名称?

安全链接仅取决于目录名称?

我目前正在努力使 nginx 仅通过文件夹名称来确保安全,而不管文件夹内的文件名称是什么。假设我正在访问文件夹 /one/two/three 中的文件,它看起来会像这样:

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

因此,文件夹“three”只能通过目录 md5 访问,而真实路径将返回 403。我有数千个这样的文件夹,所以我需要将它们隐藏,但可以通过运行时只知道 md5 的远程客户端静态访问它们。

同时,以下链接也应该有效:

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

因此只有特定目录级别是隐藏的。

答案1

您可以使用以下方法实现此目的内部位置对于您想要保护的隐藏文件夹或文件,以及检查散列代码是否允许访问您的文件的方法。

Nginx 不允许直接访问您的隐藏文件(例如 /protected/folder1/folder2/file.pdf),因为此位置已被标记为内部。但您的脚本可以使用特殊标头重定向到此位置X-Accel-重定向

因此,您可以让 Nginx 做它最擅长的事情,传递数据,并且您的脚本仅检查是否允许访问。

下面您可以看到一个简单的例子。

文件夹 /data 包含公共内容(例如公共图像)。非公共图像存储在不同的文件夹中(htdocs 之外),并通过位置 /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);
}

答案2

为了实现这一点,nginx 需要能够分辨出哈希值35d6d33467aae9a2e3dccb4b6b027878对应于three。截至今天,Nginx 还无法做到这一点(而且我认为这不在待办事项列表中)。

我能想象到的唯一能实现目标的方法相似的将会将文件托管在另一个位置,并在创建/上传文件时在您所在位置的根目录中创建以目标目录的哈希值作为链接名称的符号链接。

例如,您的网络服务器位置http://example.com/one/two/指向一个目录(例如/var/www/html/),其中符号链接指向位于另一个位置的35d6d33467aae9a2e3dccb4b6b027878目录(例如)。three//var/www/protected/

上传需要触发脚本或类似的东西以便three/在中创建文件夹/var/www/protected/,哈希“three”,然后创建符号链接/var/www/html/35d6d33467aae9a2e3dccb4b6b027878

这是我能想到的唯一办法。

相关内容