22

Say I want to encode an article title in a URL and that contains a slash. If I URL encode the article title I get:

http://example.com/articles/foo%2fbar/view/

NGINX passes this to my FastCGI application as:

http://example.com/articles/foo/bar/view/

Which rather ruins the idea.

I notice that if NGINX is serving a file, say /path/to/page.html, then it can be reached by either of the following two URLs:

http://example.com/path/to/page.html
http://example.com/path/to%2fpage.html

However this is not the case for (for example) Apache.

Is there any way to fix this behavior?

I've tried the docs and Google with no luck.

Thanks.

UPDATE

nginx config:

worker_processes  1;
pid ./nginx.pid;
events {
    worker_connections  1024;
}
http {
    server_tokens off;
    server {
        listen 80;
        server_name localhost;
        location /mysite/{
            fastcgi_pass   unix: ./mysite.fcgi.socket;

            fastcgi_param SERVER_NAME $server_name;
            fastcgi_param SERVER_PORT $server_port;
            fastcgi_param SERVER_PROTOCOL $server_protocol;
            fastcgi_param SCRIPT_NAME "/mysite/";
            fastcgi_param PATH_INFO $fastcgi_path_info;
            fastcgi_param REQUEST_METHOD $request_method;
            fastcgi_param QUERY_STRING $query_string;
            fastcgi_param CONTENT_TYPE $content_type;
            fastcgi_param CONTENT_LENGTH $content_length;
            fastcgi_pass_header Authorization;
            fastcgi_intercept_errors off;
        }
    }

}
DaedalusFall
  • 8,335
  • 6
  • 30
  • 43

4 Answers4

2

More details on this issue are provided over at Nginx pass_proxy subdirectory without url decoding, which has a full solution if you're a proxy_pass user.

With fastcgi_pass, this could happen due to the default conf/fastcgi.conf in nginx, where the DOCUMENT_URI variable is set to http://nginx.org/r/$document_uri, which is equivalent to just http://nginx.org/r/$uri, which, in turn, is the normalised (decoded and unescaped), query-less and potentially rewritten version of http://nginx.org/r/$request_uri (which, in turn, could be accessed through REQUEST_URI instead):

  fastcgi_param  REQUEST_URI        $request_uri;
  fastcgi_param  DOCUMENT_URI       $document_uri;

In your case, however, you don't actually seem to specify DOCUMENT_URI at all, since http://nginx.org/r/fastcgi_param is not inherited from a prior level if used at the current level, so, it's possible that the decoded path comes out of your http://nginx.org/r/$fastcgi_path_info, which is supposed to be paired with http://nginx.org/r/fastcgi_split_path_info, which you omit from the provided configuration, so, the original question may appear to be inconsistent, as the exact paths between the provided requests and the sample configuration don't match, either.

Regardless, the best fix with fastcgi would depend on the application, and may be one of the following:

  • Not depend on paths not being decoded and cleaned up properly by nginx. This is probably the best fix security-wise, as you're basically asking nginx to not clean up stuff like /../, either (including all the escaped variations), which is certainly intended to protect you against a whole class of vulnerabilities in your backend.
  • Redesign the whole interface to use the query parameters within QUERY_STRING to ensure paths aren't mingled or decoded prematurely.
  • Use REQUEST_URI to get the original request URI without any normalisation or decoding taking place.
  • Pay closer attention to all instances of using $uri or $document_uri, and possibly $fastcgi_path_info as well, which would usually contain the decoded and normalised paths.
  • Use the rewrite tricks as outlined in the linked answer to place undecoded $request_uri back into $uri. Note that you also might have to strip out the query string manually if you go this route.

BTW, note that what you're doing in the first place is kind of playing with fire, because it's very easy to introduce security vulnerabilities if you don't fully understand what you're doing, and if someone one day does decide to take advantage of you relying on these encoded paths bypassing proper handling and scrutiny of nginx.

The fact that what you want to do works in Apache as-is is more of a bug than a feature — this works differently in nginx by design and in order to prevent a whole class of security vulnerabilities.

cnst
  • 25,870
  • 6
  • 90
  • 122
2

Try escaping "%" as "%25"

http://example.com/articles/foo%252fbar/view/
Dayo
  • 12,413
  • 5
  • 52
  • 67
  • 2
    Not a bad idea (+1), though it feels like trying to fix the wrong bit. (Changing my escaping/unescaping rather than changing nginx's behaviour) – DaedalusFall Nov 29 '11 at 16:39
  • 2
    Well, you can either implement this fairly straightforward change or set about trying to change the core code. Look at how the tide flows and work with it or try to change the tide. It is all about choices. – Dayo Nov 29 '11 at 17:15
  • :-) I was rather hoping that it would be a simple configuration file change. As it turns out I found that passing $uri for PATH_INFO works, but you have to strip stuff off the end in code, so this also seems like the wrong thing to fix. But like you say, tides... had I not already found $uri i'd probably implement your %252f idea. – DaedalusFall Nov 29 '11 at 17:39
  • 15
    This is a bad idea: it means you're introducing a bug in your code--double-escaping--in order to work around a bug in something else. That's going in the wrong direction. – Glenn Maynard May 23 '12 at 19:50
  • Can't fathom how you can call double escaping a "bug". Anyway, it is alright to down vote and brand this as going in the wrong direction, the direction the author of Nginx himself recommends in such situations ... but I don't see any alternative solutions from you that you recommend the OP, or anyone else with this issue, implement to solve the issue while moving them in the "right direction". – Dayo May 24 '12 at 19:16
  • 1
    It's not a bug, it's a hack, but it's almost guaranteed to cause bugs in other code someone will write at some point. You can't encapsulate all access to a public web interface. But! It's apparently the best solution so you deserve a tick, which apparently you didn't get. Shame on @DaedalusFall. – mxcl Aug 30 '13 at 19:01
  • '%252f' don't work for me. It seems that nginx doesn't decode %25 as it does with %2f. So my application receives '%252f' and since it decodes once I end up getting '%2f' string. – GuiGS Oct 02 '13 at 19:00
  • 2
    RFC 3986 says: "[Implementations must not percent-encode or decode the same string more than once](https://tools.ietf.org/html/rfc3986#section-2.4)" – koppor Jul 20 '17 at 21:18
0

I had the same issue with my nginx + uWSGI + flask stack. I solved it by introducing a rewrite rule to the nginx config:

location @app {
    rewrite ./ $request_uri break;
    include uwsgi_params;
    uwsgi_pass unix:/tmp/uwsgi.sock;
}

Update: this seems to break query parameters, so I had to do this:

location @app {
    set $plain_uri $request_uri ;
    if ( $plain_uri ~ (.*)\?.* ) {
        set $plain_uri $1 ;
    }
    rewrite .* $plain_uri break;
    include uwsgi_params;
    uwsgi_pass unix:/tmp/uwsgi.sock;
}

The issue with this is that the URL originally encoded gets encoded again, so I need to unencode 4 times in total.

-1

You won't have any trouble, if you use URL query parameters. When you can control your servers routes you could go for:

http://example.com/articles/view/?path=foo%2fbar

and nginx won't touch %2f

mUnk
  • 127
  • 5