在 nginx 中编码和解码路径名

Posted

技术标签:

【中文标题】在 nginx 中编码和解码路径名【英文标题】:Encode and decode pathname in nginx 【发布时间】:2018-06-04 18:57:41 【问题描述】:

通常可以在以下位置访问文件:

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

我想对路径名 (/cats/cat1.zip) 进行编码/加密,以便链接无法正常访问,但在路径名加密/编码后可以访问:

http://example.com/Y2F0cy9jYXQxLnppcAo=

为了简单起见,我在上面使用 base64 编码,但更喜欢加密。我该怎么做?我必须编写自定义模块吗?

【问题讨论】:

【参考方案1】:

您可以使用nginx rewrite rule 重写网址(从编码到未编码)。而且,要应用您的编码逻辑,您可以使用自定义函数(我使用 perl module 完成了此操作)。

可能是这样的:

 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;
      

【讨论】:

【参考方案2】:

如果您唯一关心的是限制对某些 URL 的访问,您可以查看 this post on Securing URLs 和 Secure Link Module in 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 哈希:

    哈希之后的 URL 部分,在我们的例子中是 cat1.zip secure_link_secret 指令的参数,在本例中为 yoursecretkey

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

此外,还有一种更灵活但也更复杂的方法,通过使用secure_linksecure_link_md5 指令来使用ngx_http_secure_link_module 模块保护URL,通过IP 地址限制URL 访问,定义过期时间网址等

如果您需要完全隐藏您的网址(包括 cat1.zip 部分),您需要在以下各项之间做出决定:

    在 Nginx 端处理加密 URL 的解密 - 编写自己的,或重用别人编写的模块 在应用程序中的某处处理加密 URL 的解密 — 基本上使用 Nginx 将加密 URL 代理到应用程序,在该应用程序中解密它们并采取相应措施,如 @cnst 上面所写。

这两种方法各有利弊,但 IMO 后一种方法更简单、更灵活——一旦你设置了代理,你就不需要太担心 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 不会产生您提供的哈希值。你能详细说明原因吗?【参考方案3】:

最简单的方法是编写一个简单的后端(例如,通过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 …;

上述方法遵循“微服务”架构,因为您的解密器服务的唯一工作是执行解密和访问控制,将其留给 nginx,以通过使用内部经过特殊处理的X-Accel-Redirect HTTP 响应头。

【讨论】:

谢谢。最佳解决方案! 有用的例子:mediasuite.co.nz/blog/proxying-s3-downloads-nginx【参考方案4】:

考虑在 Lua 中使用 OpenResty 之类的东西。

Lua 几乎可以在 nginx 中做任何你想做的事情。

https://openresty.org/

https://github.com/openresty/

==== 更新 ====

现在我们有了 njs https://nginx.org/en/docs/njs/ ,用于 nginx 的 javascript,它几乎可以做任何事情。

【讨论】:

【参考方案5】:

如果有人在 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)。

【讨论】:

以上是关于在 nginx 中编码和解码路径名的主要内容,如果未能解决你的问题,请参考以下文章

什么是URL编码和URL解码

url被nginx编码了改怎么办

url被nginx编码了改怎么办

h5房卡源码C语言实现哈夫曼树编码解码及问题总结

javascript中可用的编码解码函数

Huffman编码和解码