在 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_link
和secure_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 中编码和解码路径名的主要内容,如果未能解决你的问题,请参考以下文章