Saturday, January 6, 2018

Encode and decode pathname in nginx

Leave a Comment

Normally files can be accessed at:

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

I want to encode/encrypt the pathname (/cats/cat1.zip) so that the link is not normally accessible but accessible after the pathname is encrypted/encoded:

http://example.com/Y2F0cy9jYXQxLnppcAo=

I'm using base64 encoding above for simplicity but would prefer encryption. How do I do about doing this? Do I have to write a custom module?

4 Answers

Answers 1

If your only concern is limiting access to certain URLs you may take a look at this post on Securing URLs with the Secure Link Module in Nginx.

It provides fairly simple method for securing your files — the most basic and simple way to encrypt your URLs is by using the secure_link_secret directive:

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;     } } 

The URL to access cat1.zip file will be http://example.com/cats/80e2dfecb5f54513ad4e2e6217d36fd4/cat1.zip where 80e2dfecb5f54513ad4e2e6217d36fd4 is the MD5 hash computed on a text string that concatenates two elements:

  1. The part of the URL that follows the hash, in our case cat1.zip
  2. The parameter to the secure_link_secret directive, in this case yoursecretkey

The above example also assumes the files accessible via the encrypted URLs are stored at /path/to/secret/files/secure directory.

Additionally, there is a more flexible, but also more complex method, for securing URLs with ngx_http_secure_link_module module by using the secure_link and secure_link_md5 directives, to limit the URL access by IP address, define expiration time of the URLs etc.

If you need to completely obscure your URLs (including the cat1.zip part), you'll need to make a decision between:

  1. Handling the decryption of the encrypted URL on the Nginx side — writing your own, or reusing a module written by someone else
  2. Handling the decryption of the encrypted URL somewhere in your application — basically using Nginx to proxy your encrypted URLs to your application where you decrypt them and act accordingly, as @cnst writes above.

Both approaches have pros and cons, but IMO the latter one is simpler and more flexible — once you set up your proxy you don't need to worry much about Nginx, nor need to compile it with some special prerequisites; no need to write or compile code in language other than what you already are writing in your application (unless your application includes code in C, Lua or Perl).

Here's an example of a simple Nginx/Express application where you'd handle the decryption within your application. The Nginx configuration might look like:

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;     } } 

and on the application (Node.js/Express) side you may have something like:

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); 

The above example assumes that the secured files are located at /path/to/secured/files directory. Also it assumes that if the URL is accessible (properly encrypted) you are sending the files for download, but the same logic would apply if you need to do something else.

Answers 2

The easiest way would be to write a simple backend (with interfacing through proxy_pass, for example) that would decrypt the filename from the $uri, and provide the results within the X-Accel-Redirect response header (which is subject to proxy_ignore_headers in nginx), which would subsequently be subject to an internal redirect within nginx (to a location that cannot be accessed without going through the backend first), and served with all the optimisations that are already part of nginx.

location /sec/ {     proxy_pass http://decryptor/; } location /x-accel-redirect-here/ {     internal;     alias …; } 

The above approach follows the ‘microservices’ architecture, in that your decryptor service's only job is to perform decryption and access control, leaving it up to nginx to ensure files are served correctly and in the most efficient way possible through the use of the internal specially-treated X-Accel-Redirect HTTP response header.

Answers 3

Consider using something like OpenResty with Lua.

Lua can do almost everything you want in nginx.

https://openresty.org/

https://github.com/openresty/

Answers 4

You can use a Nginx rewrite rule rewrite the url (from encoded to unencoded). And, to apply your encoding logic you can use a custom function (I did it with the perl module).

Could be something like this:

 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;       } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment