Project

General

Profile

[Solved] mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs?

Added by epe over 6 years ago

Hello,

I noticed that if I send an HTTP request containing an absolute-form of the request-target with the scheme part in all-caps, lighttpd treats the request-target as an absolute-path instead of an absolute-URI.

I've configured lighty to forward all requests to a mod_fcgi backend that's simply echoing the environment variables in the response, and if I send this request:

GET HTTP://www.example.com/ HTTP/1.1
Host: www.example.com
Connection: close

the fastCGI application will have "/HTTP:" in SCRIPT_NAME instead of the expected "/". PATH_INFO will have the value "/www.example.com/" instead of empty, and REQUEST_URI is set to "HTTP://www.example.com/", instead of "/".

Surely this isn't desired behavior, right? Any thoughts?

-Thanks


Replies (7)

RE: mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs? - Added by gstrauss over 6 years ago

Surely this isn't desired behavior, right? Any thoughts?

RFC 7230: "The scheme and host are case-insensitive and normally provided in lowercase; all other components are compared in a case-sensitive manner."

You should use lowercase, but here is a patch to try if you insist on using uppercase.

--- a/src/request.c
+++ b/src/request.c
@@ -587,13 +587,13 @@ int http_request_parse(server *srv, connection *con) {
                                        return 0;
                                }

-                               if (0 == strncmp(uri, "http://", 7) &&
+                               if (0 == strncasecmp(uri, "http://", 7) &&
                                    NULL != (nuri = strchr(uri + 7, '/'))) {
                                        reqline_host = uri + 7;
                                        reqline_hostlen = nuri - reqline_host;

                                        buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
-                               } else if (0 == strncmp(uri, "https://", 8) &&
+                               } else if (0 == strncasecmp(uri, "https://", 8) &&
                                    NULL != (nuri = strchr(uri + 8, '/'))) {
                                        reqline_host = uri + 8;
                                        reqline_hostlen = nuri - reqline_host;

RE: mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs? - Added by stbuehler over 6 years ago

We should fix that :)

Also: why are we accepting paths which don't start with '/' (apart from the magic *) ? This should fail imho.

RE: mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs? - Added by epe over 6 years ago

Also: why are we accepting paths which don't start with '/' (apart from the magic *) ? This should fail imho.

Seconded!

You should use lowercase, but here is a patch to try if you insist on using uppercase.

Not that I insist, but I use curl for testing my fcgi app, and if you want curl to send an absolute-URI by specifying lighty as a proxy server and you gave it a hostname without the "http://", curl will build the absolute-URI with "HTTP://" plus the hostname. That's how I ran into this problem.

Anyways, thanks for the patch!

RE: [Solved] mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs? - Added by gstrauss over 6 years ago

We should fix that :)

Yes, of course. The RFC specifies it should be allowed. I'll submit the patch above.

Also: why are we accepting paths which don't start with '/' (apart from the magic *) ? This should fail imho.

Both this and the case-sensitive bug in scheme have been present since first import of code into 1.4 branch.

buffer_path_simplify() prepends the '/' if one is not present, but con->uri.path_raw contains the original URI and is accessible in mod_accesslog, mod_magnet, and mod_webdav. HTTP/1.1 permits fully qualified URIs, and lighttpd core might not recognize a given scheme, which does not begin with '/', but could be recognized by a custom module. In practice, lighttpd currently expects only http:// or https:// in fully qualified URIs, and would need to be modified to accept even ws:// or wss://.

RE: [Solved] mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs? - Added by stbuehler over 6 years ago

If we want to accept those unknown schemes we should still parse them in a sane way; handling them by prefixing with '/' seems a bad idea to me. (And I didn't want to blame this on you, I suspected this was an older problem, sorry it came out wrong). Maybe for 1.4.47?

RE: [Solved] mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs? - Added by gstrauss over 6 years ago

It would not be difficult to add something like server.scheme-allowed = ( "http", "https" ) array list with the default of "http" and "https", or to always allow those schemes. However, there is other code which checks for "https" and assumes "http" if no match, so some other code would need to be modified to be multi-scheme aware.

Simply rejecting schemes URIs which do not begin with '/' or "http://" or "https://" is probably a sane choice for most people, but is still a behavior change, so if we're planning to do that, then we should announce the planned behavior change (targetting 1.4.47) in the 1.4.46 release notes. Of course, such a change is also expected to have low impact since most request-URI (other than through forward proxies) are not fully qualified URIs, and those that are usually begin "http://" or "https://"

RE: [Solved] mod_fcgi: treat scheme as case-insensitive when parsing absoluteURIs? - Added by gstrauss over 6 years ago

I have committed code to my dev branch which more strictly validates the beginning of the request-URI unless server.http-parseopt-header-strict = "disable" (default is enabled)

--- a/src/request.c
+++ b/src/request.c
@@ -635,9 +635,15 @@ int http_request_parse(server *srv, connection *con) {
                                        reqline_hostlen = nuri - reqline_host;

                                        buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
-                               } else {
+                               } else if (!http_header_strict
+                                          || (HTTP_METHOD_OPTIONS == con->request.http_method && uri[0] == '*' && uri[1] == '\0')) {
                                        /* everything looks good so far */
                                        buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
+                               } else {
+                                       con->http_status = 400;
+                                       con->keep_alive = 0;
+                                       log_error_write(srv, __FILE__, __LINE__, "ss", "request-URI parse error -> 400 for:", uri);
+                                       return 0;
                                }

                                /* check uri for invalid characters */

    (1-7/7)