在nginx中对路径名进行编码和解码

14

通常情况下,文件可以通过以下链接进行访问:

http://example.com/cats/cat1.zip

我想对路径名(/cats/cat1.zip)进行编码/加密,以使链接在未经编码/加密的情况下无法访问,但在对路径名进行编码/加密后可访问:

http://example.com/Y2F0cy9jYXQxLnppcAo=

上述使用了base64编码仅为简单起见,但我更希望进行加密。我该如何做?需要编写自定义模块吗?

5个回答

8
如果您只关心限制对特定URL的访问,可以参考此处有关保护URL的帖子以及Nginx中的安全链接模块。它提供了一种相当简单的方法来保护您的文件-加密URL最基本和简单的方式是使用secure_link_secret指令:
server {

    listen 80;
    server_name example.com;

    location /cats {
        secure_link_secret yoursecretkey;
        if ($secure_link = "") { return 403; }


        rewrite ^ /secure/$secure_link;
    }


    location /secure {
        internal;
        root /path/to/secret/files;
    }
}

访问cat1.zip文件的URL将是http://example.com/cats/80e2dfecb5f54513ad4e2e6217d36fd4/cat1.zip,其中80e2dfecb5f54513ad4e2e6217d36fd4是在连接两个元素的文本字符串上计算出来的MD5哈希值:

  1. 在我们的情况下,哈希后面的URL部分为cat1.zip
  2. secure_link_secret指令的参数,在这种情况下为yoursecretkey

以上示例还假设通过加密URL可访问的文件存储在/path/to/secret/files/secure目录中。

此外,使用ngx_http_secure_link_module模块用secure_linksecure_link_md5指令更灵活、但也更复杂,可以通过IP地址限制URL访问,定义URL的过期时间等。

如果您需要完全隐藏URL(包括“cat1.zip”部分),则需要在以下两种方案之间做出决策:
1. 在Nginx端处理加密URL的解密 - 编写自己的模块或重用他人编写的模块。 2. 在应用程序中某处处理加密URL的解密 - 基本上使用Nginx将加密URL代理到您的应用程序,在那里进行解密并相应地采取行动,如@cnst所述。
这两种方法都有优缺点,但在我看来,后者更简单、更灵活 - 一旦设置了代理,就不需要太担心Nginx,也不需要编译带有某些特殊先决条件的Nginx;除非您的应用程序包含C、Lua或Perl代码,否则无需编写或编译其他语言的代码。
以下是一个简单的Nginx/Express应用程序示例,您可以在其中处理应用程序内的解密。Nginx配置可能如下:
server {

    listen 80;
    server_name example.com;

    location /cats {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-NginX-Proxy true;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://127.0.0.1:8000;
    }

    location /path/to/secured/files {
        internal;
    }
}

在应用程序(Node.js/Express)端,您可能会有类似以下的内容:

const express = require ('express');
const app = express();

app.get('/cats/:encrypted', function(req, res) {
  const encrypted = req.params.encrypted;

  // 
  // Your decryption logic here
  //
  const decryptedFileName = decryptionFunction(encrypted);

  if (decryptedFileName) {
    res.set('X-Accel-Redirect', `/path/to/secured/files/${decryptedFileName}`);
  } else {
    // return error
  }
});


app.listen(8000);

上面的示例假定安全文件位于/path/to/secured/files目录中。同时,它假设如果URL是可访问的(正确加密),则您正在发送文件进行下载,但是如果您需要执行其他操作,则相同的逻辑也适用。

看起来 echo -n 'cat1.zipyoursecretkey' | openssl md5 -hex 并没有产生你提供的哈希值。你能详细说明一下原因吗? - Floam

7
您可以使用Nginx重写规则来将URL从编码转换为未编码的格式。而且,您可以使用自定义函数来应用您的编码逻辑(我是使用Perl模块完成的)。
以下是可能的示例:
 http {
  ...
    perl_modules perl/lib;
    ...
    perl_set $uri_decode 'sub {
      my $r = shift;
      my $uri = $r->uri;
      $uri = perl_magic_to_decode_the_url;
      return $uri;
    }';
    ...
    server {
    ...
      location /your-protected-urls-regex {
        rewrite ^(.*)$ $scheme://$host$uri_decode;
      }

5

4
最简单的方法是编写一个简单的后端程序(例如通过proxy_pass进行接口交互),从$uri中解密文件名,并将结果放在X-Accel-Redirect响应头中(该响应头可以通过nginx中的proxy_ignore_headers进行忽略),然后在nginx内部进行internal重定向(到无法直接访问而需要先通过后端程序的位置),并使用nginx已经具备的所有优化功能提供服务。
location /sec/ {
    proxy_pass http://decryptor/;
}
location /x-accel-redirect-here/ {
    internal;
    alias …;
}

以上方法采用了“微服务”架构,即您的解密服务的唯一工作是执行解密和访问控制,而将文件的正确提供和以最高效的方式通过内部特殊处理的X-Accel-Redirect HTTP响应头由nginx来保证。


谢谢。最佳解决方案! - Protect children of Donbas2014
有用的例子:https://www.mediasuite.co.nz/blog/proxying-s3-downloads-nginx/ - Pallav Jha

0
如果有人在Nginx中遇到了取消转义查询参数的问题,这是我在Ubuntu 20.04上标准Nginx设置中解决它的方法(url中的查询参数为foo,如https://some.domain.com/some/path?foo=some%2Fquery%2Fvalue):
  perl_modules perl/lib;

  perl_set $arg_foo_decoded 'sub {
    my $r = shift;
    my $arg_foo = $r->variable("arg_foo");
    $arg_foo =~ s/\+/ /ig;
    my $arg_foo_decoded = $r->unescape($arg_foo);
    return $arg_foo_decoded;
  }';

我可以在我的location块中使用$arg_foo_decoded变量。它现在包含some/query/value

编辑

添加了一行代码,以确保与包含空格编码为+的表单值兼容(参见:https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接