Because form autocomplete temporarily disables clean URLs, this unexpected behavior can cause sites to lose tagging and author autocompletion capabilities if they rely on clean URLs for the autocomplete path to work, such as sites that have an index.html file or that redirect '/' from https to http. Ensuring that index.php is part of the explicit path in this case resolves these issues.

Background

Autocomplete fields throw the follow error:
An AJAX HTTP error occurred.
HTTP Result Code: 200
Debugging information follows.
Path: http://website/?q=eng/taxonomy/autocomplete/...

Autocomplete was working perfectly before upgrading to Drupal core 7.39 and chaos tools 1.8.

After some debugging the error is caused by the path set:
http://website/?q=eng/taxonomy/autocomplete/
This works if index.html doesn't exist, as it would be read by index.php automatically (even when a 301 http status message would be throw and then a redirect to http://website/eng/taxonomy/autocomplete/.. ).

To make it work with the index.html present on the site, I had to remove the '?q=' from the path.
This could be done updating the variable db.uri in misc/autocomplete.js (line 300).
But I opted to do it before the javascript on the includes/form.inc (line 4013), simply added $attributes['value'] = str_replace("?q=", "", $attributes['value']);
This is a temporal fix until the change from the Drupal core affecting the $variables['element'] ['#autocomplete_input']['#url_value'] is tracked down.

UPDATE
After tracking down the path the culprit is the function form_process_autocomplete, as this function is forcing to use non-clean urls. Return a non clean url when index.html exist causes the issue.
A lot of sites use index.html as splash page, and less frequent but there are sites that have other pages at the same level of the Drupal install. so the server index is not necessarily index.php. The server DirectoryIndex can be set to any type of document, (not only index.php or index.html - more info refer to: https://httpd.apache.org/docs/2.4/mod/mod_dir.html ).

The solution is to use index.php in the autocomplete path, it stills working using non-clean urls and it won't matter the server DirectoryIndex option set.

Comments

Blanca.Esqueda created an issue. See original summary.

blanca.esqueda’s picture

After tracking down the path the culprit is the function form_process_autocomplete, as this function is forcing to use non-clean urls. Return a non clean url when index.html exist causes the issue.

// Force autocomplete to use non-clean URLs since this protects against the
// browser interpreting the path plus search string as an actual file.
$current_clean_url = isset($GLOBALS['conf']['clean_url']) ? $GLOBALS['conf']['clean_url'] : NULL;
$GLOBALS['conf']['clean_url'] = 0;
$element['#autocomplete_input']['#url_value'] = url($element['#autocomplete_path'], array('absolute' => TRUE));
$GLOBALS['conf']['clean_url'] = $current_clean_url;

blanca.esqueda’s picture

Status: Active » Needs review
StatusFileSize
new878 bytes

Please review the patch attached.

A lot of sites use index.html as splash page, and less frequent but there are sites that have other pages at the same level of the Drupal install. So the server index not necessarily is index.php

I tested setting a condition to check if index.html was present and avoid the non-clean urls, and it worked.

But after thinking a bit more, the server DirectoryIndex can be set to any type of document, (not only index.php or index.html ).
refer to: https://httpd.apache.org/docs/2.4/mod/mod_dir.html

So the solution is to use index.php in the autocomplete path, this still working using non-clean urls and it won't matter the server DirectoryIndex option set.

Status: Needs review » Needs work

The last submitted patch, 4: autocomplete_function-2599326-4.patch, failed testing.

blanca.esqueda’s picture

Status: Needs work » Needs review
StatusFileSize
new902 bytes

Status: Needs review » Needs work

The last submitted patch, 6: autocomplete_function-2599326-5.patch, failed testing.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 6: autocomplete_function-2599326-5.patch, failed testing.

blanca.esqueda’s picture

Status: Needs work » Needs review
StatusFileSize
new902 bytes

patch status is failed because is not passing the profile.test

Message Group Filename Line Function Status
Autocomplete found. Other profile.test 352 ProfileTestAutocomplete->testAutocomplete()

the patch is checking for and url without index.php, so and update to the profileTestAutocomplete is necessary as well.

blanca.esqueda’s picture

StatusFileSize
new1.73 KB

Ups... I forgot to include the new profileTestAutocomplete test.

The last submitted patch, 10: autocomplete_function-2599326-10.patch, failed testing.

blanca.esqueda’s picture

Issue summary: View changes
blanca.esqueda’s picture

Issue summary: View changes
cilefen’s picture

Priority: Major » Normal

A lot of sites use index.html as splash page, and less frequent but there are sites that have other pages at the same level of the Drupal install. So the server index not necessarily is index.php

I have not ever heard of someone running Drupal like that.

I am moving this to "Normal" priority to signify a smaller scope because someone would have to place an index.html file in a Drupal installation for this to occur and that could cause other problems besides this one depending on the web server configuration. I argue that do to this is technically "hacking core" because a file has been placed in a Drupal core directory that doesn't belong.

blanca.esqueda’s picture

Not necessarily is hacking Drupal.
The government of Canada requires WetKit for their sites, and time ago it was an issue (and even recently) adding splash pages. So the index.html was a workaround.
( ie. https://github.com/wet-boew/wet-boew-drupal/issues/878 )

I agree it is a small scope, but it is still a bug. And a big one for that small scope because all the autocomplete fields stopped working and generate ajax errors.
I have't seen index.html causing other problems, beside Drupal works base on index.php so adding index.php to the autocomplete path would only make sure that it is pointing to the correct place. The autocomplete url path is used as absolute ( url($element['#autocomplete_path'], array('absolute' => TRUE)); ), and an absolute path should be a full path and contain the file it is pointing to.

David_Rothstein’s picture

Version: 7.39 » 7.x-dev
StatusFileSize
new2.04 KB

Yeah, this is a pretty unusual setup. Normally it's the responsibility of the site itself to handle these situations (see documentation of the 'script' option in url()) but I can see the argument that we should take care of it here since this is being run on sites that don't use non-clean URLs otherwise.

We should just use the 'script' option to do this, though, I think. See attached.

blanca.esqueda’s picture

Version: 7.x-dev » 7.39

Thank you @David_Rothstein,
I wasn't aware of the script argument on the url function. everyday I learn something new!
I'm applying and testing right now your patch.

cilefen’s picture

Version: 7.39 » 7.x-dev

This is my standard cut-and-paste on the backport policy, it may apply here:

Please check if this is an issue with Drupal 8. If so, it must be fixed there first according to the backport policy. If it is, move it to branch 8.0.x-dev and tag it “Needs backport to D7”. There may also be an existing Drupal 8 issue. Try to find it. If one exists, tag it “Needs backport to D7”. If it is open, offer a patch. If its status is “Fixed”, make its status “Patch (to be ported)”, move it to version 7.x-dev and upload your latest patch if one exists.

blanca.esqueda’s picture

Status: Needs review » Reviewed & tested by the community

Tested and it worked!

Applied the patch to a site using clean urls and to a site with non-clean urls, it fixed the issue in one and the other one continued working perfectly.

blanca.esqueda’s picture

@cilefen thanks for the note. checking Drupal 8 right now.

natew’s picture

If you are using the wetkit distribution: https://www.drupal.org/project/wetkit

There is a contrib module (spashify) included for this. There was some discussion about this a couple years back on github for the distribution on how to implement a spash page.: https://github.com/wet-boew/wet-boew-drupal/issues/280

blanca.esqueda’s picture

Thank you @natew, I previously read that post and more other regarding the wetkit distribution and splash page.

But as @David_Rothstein commented:

but I can see the argument that we should take care of it here since this is being run on sites that don't use non-clean URLs otherwise.

There are sites that autocomplete stopped working and not all of them have a maintenance team to handle changes. If this was working on previous Drupal versions, there is not harm to make it work again if it requires a small code update only.

David_Rothstein’s picture

Regarding Drupal 8, I think this issue probably isn't relevant there since most of the autocomplete security fix from Drupal 7.39 didn't need to be applied to Drupal 8 (see #2554221: Port Cross-site Scripting - Autocomplete system from SA-CORE-2015-003 to Drupal 8).

blanca.esqueda’s picture

@David_Rothstein, i didnt find either the autocomplete functions on drupal8 includes/form.inc
Also I tried to find if the same override clean-url was used somewhere else and I wasn't able to find it.
I checked directly the autocomplete path from an autocomplete field on drupal8 and the path is relative. The ?q= is not being assigned.

Same as you I don't think this would apply for Drupal8.
I think this would apply only to Drupal7.

mlncn’s picture

Component: taxonomy.module » forms system
Issue summary: View changes
Issue tags: +DrupalWTF

We ran into this same issue on a site configured to redirect the home page, "/", to http but to otherwise leave well enough alone. (It redirects user, contact, subscribe, node/add and other form-containing paths to https.) So when taxonomy or user (author) autocomplete ignores clean URLs, the form redirects the slash-starting path (/?q=...) over to http and it fails as a security threat: "An AJAX HTTP error occurred. HTTP Result Code: 302 Debugging information follows. Path: https://example.org/?q=user/autocomplete StatusText: Found ResponseText: 302 Found Found The document has moved here. Apache/2.4.10 (Debian) Server at example.org Port 443"

While this can also be fixed by getting clever about redirect rule conditions that check for the query string, this patch will fix this problem also and i think that would be A Nice Thing. :-)

dinarcon’s picture

Hi,

In a scenario like the one described by mlncn in #26, this patch solves the issue. Particularly, I have the node creation pages on HTTPS. The autocomplete provided by core was configured to post to https://portside.org/?q=taxonomy/autocomplete/field_name which was rewrote by the server to http://portside.org/?q=taxonomy/autocomplete/field_name Having the HTTPS page query an HTTP resource produced a mixed content error which effectively blocks the operation.

Even though I was able to apply the patch locally and it fixed the issue, my production environment is set up in a way in which I cannot modify Drupal's core files. So I was able to replicate the patch functionality using hook_url_outbound_alter() as follows:

function MODULE_url_outbound_alter(&$path, &$options, $original_path) {
  if (strpos($path,'autocomplete') !== false && empty($options['script'])) {
    $options['script'] = 'index.php';
  }
}

Once this patch lands this hook implementation might not be needed. Meanwhile, if someone has a limitation that prevents patching core files, the previous hook implementation should do the trick.

David_Rothstein’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +7.42 release notes

Committed to 7.x - thanks!

  • David_Rothstein committed 2551236 on 7.x
    Issue #2599326 by Blanca.Esqueda, David_Rothstein: Fix autocomplete...
chellman’s picture

I ran into an issue with this fix that I want to document here. On a server running nginx, with direct access to the PHP files blocked, this fix breaks any link that doesn't use clean URLs. Allowing direct access to index.php fixes the issue using something like this in your nginx conf:

# Allow direct access to index.php
location = /index.php {
  fastcgi_pass phpcgi;
}
okolobaxa’s picture

Problem is still exists for me.
I use perusio nginx config (https://github.com/perusio/drupal-with-nginx) and Globalredirect module. On autocomplete ajax request is sended to url http://oldsaratov.ru/index.php?q=admin/views/ajax/autocomplete/taxonomy/... instead of http://oldsaratov.ru/admin/views/ajax/autocomplete/taxonomy/1/test

Is it problem nginx configuration, that block direct access to index.php ?

David_Rothstein’s picture

On a server running nginx, with direct access to the PHP files blocked, this fix breaks any link that doesn't use clean URLs.

I assume you mean it breaks autocomplete URLs? (I don't see how the change in this issue could break links in general.)

I could put something in the release notes about this (or we could consider rolling it back in favor of a different solution) but I'm trying to understand the issue first... what is the use case for blocking direct access to Drupal's index.php file in the first place? That seems pretty unusual to me. Is it part of an attempt at denial-of-service protection at the Nginx level?

The original fix here was for an edge case also, but one that has some justification in the earlier comments.

Note that if you don't want to change your Nginx configuration, it should be possible to use hook_url_outbound_alter() in site-specific code to force the 'script' option to url() to always be empty - i.e. similar to what was discussed above (before this patch was committed) for forcing it to always be 'index.php', but essentially the opposite of that.

chellman’s picture

In my case, yes, it was the autocomplete URLs, or anything else that would theoretically force index.php into the path.

Disallowing direct script access is a security feature of perusio's configs, which I also use:
https://github.com/perusio/drupal-with-nginx#security-features

I'm not enough of a security guy to know what the justification is, but I don't think it's crazy to expect index.php to work, so I personally don't think it's worth a rollback. I just wanted to document the issue in case anyone else noticed. Between a config change, or using hook_url_outbound_alter(), it should be fine.

omega8cc’s picture

Now this is a Drupal core bug / regression which has to be fixed in core. Not just popular Nginx configs, like BOA but Aegir forces clean URLs by default on all hosted sites, and Drupal core should be compatible with its own feature, and not just selectively "ignore" it.

[EDIT] I mean, if Drupal core provides clean URLs as a feature we can depend on, and we force clean URLs by default, plus we deny direct access to index.php, so we can properly filter all requests before they hit index.php as an internal location in Nginx, but core at some point decides to:

1. selectively ignore its own feature and force non-clean URLs selectively
2. then the "fix" for this weird method of fixing other problems assumes that index.php can be always hit directly

then we have a regression on top of another regression.

I couldn't consider this chain of "fixes" as "fixed".

lunk rat’s picture

Status: Fixed » Needs work

This needs attention. Autocomplete widget is broken in 7.42 for any site configured to force clean URLs or block direct access to index.php.

blanca.esqueda’s picture

As commented by David_Rothstein:

The original fix here was for an edge case also, but one that has some justification in the earlier comments.

This patch was needed, and resolved the autocomplete issue on more than one case (original issue, +comment #26). Functionality that was working before upgrade to Drupal 7.39.

But similar situation is happening for people running nginx were now they are the ones having issues on Drupal 7.42

Re-rolling back the patch is not a solution, so something that works for both cases should be worked out.

Note: the only thing that I don't get, it is how index.pho could be blocked to be access? Doesn't Drupal run using index.php?

David_Rothstein’s picture

@omega8cc, Drupal core does not allow non-clean URLs to be disabled - they are always supposed to be functional. There are a couple other places in the codebase (for example here and other places in update.php) that assume that, and there's even a test for it.

But if you have an idea for another way to fix the autocomplete security issue (from https://www.drupal.org/SA-CORE-2015-003) that doesn't involve using non-clean URLs, it's definitely worth creating an issue for that.

David_Rothstein’s picture

Status: Needs work » Fixed

plus we deny direct access to index.php, so we can properly filter all requests before they hit index.php as an internal location in Nginx

What is the reason for denying access completely, rather than just intercepting the request and/or redirecting it?

In any case, for now I've added a note about this in the "Known issues" section of https://www.drupal.org/drupal-7.42-release-notes. Feel free to suggest any edits.

I think we should consider this issue fixed for now; I don't think we should roll this back unless there's a really compelling use case for blocking requests to index.php that I'm not understanding. But even so, definitely create a followup issue if there's a way to improve this.

omega8cc’s picture

@David_Rothstein BOA and other popular Nginx configurations don't really "force" clean URLs in the way which could prevent them from working. These popular configurations used by thousands of live Drupal sites simply force $conf['clean_url'] = 1; but non-clean URLs still work, as long as these requests don't *require* /index.php present in the URI.

I have checked BOA configuration again, and in fact we have removed this restriction a long time ago, so /index.php *can* be accessed directly, but we have a rewrite redirect in place to automatically convert such requests to clean URLs, mainly for SEO purposes, extension-free URI, by popular demand.

###
### Rewrite legacy requests with /index.php to extension-free URL.
###
if ( $args ~* "^q=(?<query_value>.*)" ) {
  rewrite ^/index.php$ $scheme://$host/?q=$query_value? permanent;
}

This rewrite redirect makes your fix in this issue breaking things for BOA users. We don't need this fix, because BOA does recognize /index.php by default.

I don't have any quick fix for this, so I guess we will revert this change in the D7 core used in BOA to prevent problems.

Again, I strongly disagree with the method of fixing problems for edge-case configurations (like with index.html instead of index.php recognized by default) by changes in core which break things potentially for everyone else.

blanca.esqueda’s picture

@omega8cc

I don't consider the fix as edge-case... Similar as your situation, autocomplete was working perfectly before a Drupal version update.
In our case specifically before the 7.39 version, the changes was regarding a security issue. The non-clean autocomplete url was forced and broke things for some people.

I could say that a server configuration (like redirect in place to automatically convert request to extension-free) would be an edge-case as well.
And even I could quote you:

I strongly disagree with the method of fixing problems for edge-case configurations (like redirect in place to automatically convert request to extension-free) by changes in core which break things potentially for everyone else.

But as commented before I think the solution is to create a patch that works for everyone.

Before version 7.39, autocomplete was working for everyone.
So we know that not forcing a non-clean autocomplete url would resolve this issue for everyone, but it has security risks. So let's focus on find a way to mitigate that security risk when using clean urls.

I read that the security risk is that files were able to be added using the clean-url, so I'll play around and hopefully a solution that resolves everyones concerns could be found. If someone else is able to look for a solution as well, that would be great!

David_Rothstein’s picture

I have checked BOA configuration again, and in fact we have removed this restriction a long time ago, so /index.php *can* be accessed directly, but we have a redirect in place to automatically convert such requests to extension-free URI

Hm... then why is the patch that was committed here causing you a problem at all? It should work fine with a redirect like that.

What should happen is that Drupal requests http://example.com/index.php?q=path/to/autocomplete, your nginx configuration redirects that to http://example.com/?q=path/to/autocomplete, and the server responds to that request.

I don't have nginx handy to test this out, but I just tested a similar redirect rule for Apache (which I got from here):

RewriteCond %{THE_REQUEST} ^GET.*index\.php [NC]
RewriteRule (.*?)index\.php/*(.*) /$1$2 [R=301,NE,L]

The redirect happened as expected, and autocomplete worked fine.

Do you have any more information on why it isn't working?

omega8cc’s picture

@Blanca.Esqueda It is a true, very edge case if Drupal site is not configured to recognize index.php by default, and instead uses some index.html by default. The subtle difference here is that BOA edge case didn't require patching core with any fix, while the edge case discussed here "required" it, although I would consider this to be a problem to be fixed on the server admin side and not in Drupal core.

@David_Rothstein Not really sure why, but reverting the patch from this issue resolved the problem for us, although it was only done to confirm that indeed this change in core broke things for BOA users. Furthermore, we can't force everyone to use our modified D7 core: https://github.com/omega8cc/7x and BOA users are free to use vanilla D7, so they would hit this problem anyway.

I think that the solution, at least for BOA and others who use similar Nginx configuration is to add an extra location for autocomplete above that redirect we have in place, so Nginx could ignore the redirect on autocomplete requests only. Something like:

###
### Workaround for https://www.drupal.org/node/2599326.
###
if ( $args ~* "/autocomplete/" ) {
  return 405;
  ### error_page 405 = @drupal; ### error_page directive is not allowed within pseudo-location
}
error_page 405 = @drupal;

###
### Rewrite legacy requests with /index.php to extension-free URL.
###
if ( $args ~* "^q=(?<query_value>.*)" ) {
  rewrite ^/index.php$ $scheme://$host/?q=$query_value? permanent;
}

Here we are using a trick to prevent matching the next location with fake error_page directive which sends the redirect request to @drupal location, which then transfers the request to /index.php anyway, after some bot/crawler filtering.

I will check now if this quick idea works in reality.

David_Rothstein’s picture

@David_Rothstein Not really sure why, but reverting the patch from this issue resolved the problem for us, although it was only done to confirm that indeed this change in core broke things for BOA users.

It would definitely be great if someone has time to debug this and find out why. Like I said above, I can see why the patch in this issue would not work for a site that actually blocks index.php, but I can't see why it would fail for a site that just redirects index.php requests elsewhere... If that's not working, then it seems like something (either in core or in the nginx configuration) isn't working the way we think it is.

omega8cc’s picture

@David_Rothstein -- Yes, it is rather weird, so I will debug this further and share the results here.

cafuego’s picture

For the record, the snippet from #42 seems to do the trick for autocomplete.

cudevdev’s picture

Opened a new issue here: https://www.drupal.org/node/2671940

Anonymous’s picture

I consider perusio nginx config to be the best out there and even if Nginx was not a mainstream http server when Drupal 7 came out it gained huge amounts of market share up until now and i think it should not be considered "edge case" to some http nerds. I really liked to have direct access to any .php file disabled with nginx.

On the other hand autocomplete was working perfectly over years with this kind of configuration. There is no warning or statement on the settings page of the "Clean URL" feature, that by enabling it Drupal will only use it partly as it was not the case. Is there any hope to see Drupal to go back into using clean URLs as it was before?

David_Rothstein’s picture

I really liked to have direct access to any .php file disabled with nginx.

Per #38, I'm still looking for an explanation of why blocking index.php entirely (i.e. sending it to a 404 or 403) is a reasonable configuration Drupal should support.

But again, I definitely agree that redirecting index.php requests elsewhere (so "index.php" doesn't appear in the URL) is reasonable, so if autocomplete requests aren't working because of that we should fix it. #41 was my best attempt to debug that, and I couldn't figure out how to reproduce a problem.

David_Rothstein’s picture

Also if it helps, here's more details on the code I suggested in #32 that can be used to undo the change from this issue at the Drupal level. If you put something like this in a custom module (and change "MYMODULE" to the name of the module) I think it should work. Based on some brief testing I just did now it seemed to.

/**
 * Implements hook_url_outbound_alter().
 */
function MYMODULE_url_outbound_alter(&$path, &$options, $original_path) {
  if (isset($options['script']) && $options['script'] == 'index.php') {
    $options['script'] = '';
  }
}

Basically if for whatever reason you really want to try to ensure that Drupal itself never generates a link with "index.php" in it on your site, the above seems to me like the most robust way to do it.

David_Rothstein’s picture

Adding the issue from #46 as a related issue.

Anonymous’s picture

Per #38, I'm still looking for an explanation of why blocking index.php entirely (i.e. sending it to a 404 or 403) is a reasonable configuration Drupal should support.

This is not about index.php file itself. This is more about the general fact that i can (and have) prevented access to any PHP file for security reasons. Perusio suggested this with a very simple (*.php) filter and it also blocks update.php and install.php as it does with any other PHP file. Additionall i have filters for *.txt files *.module, *.inc and so on. I want to make sure people only access those files i want them to access especially since Drupal mostly runs with lots of 3rd party modules with various files, coding and security standards. Nginx was designed to work this way (if you want to), why should i not use it if you want people (and search engines) to only read clean URLs anyway?

omega8cc’s picture

@David_Rothstein -- after some testing it seems that there must be something else involved, like specific contrib module, which triggers the problem with broken autocomplete for some BOA / Nginx users, because in our tests with some popular distros, like Commerce Kickstart + vanilla D7 and our customized D7 we couldn't reproduce the problem, and there was no need to add that extra pseudo-location, plus the redirect to index.php-free URI works and doesn't break anything, so we are hunting some ghosts here, apparently.

@cafuego -- could you share more details on the exact URI path where #42 helps? As I have explained above, it gets a bit mysterious, because there must be something else which generates the problem and needs this fix, not just the change in the core.

I will report back if we will find reliable method to reproduce the problem.

Anonymous’s picture

#49, thanks! This is a much better workaround then patching core. At least for the time being.

David_Rothstein’s picture

@omega8cc, thanks - that is useful info. So do we know if people who are experiencing the problem are experiencing it for a particular autocomplete on a particular form, or on all of them?

@sint, glad it was helpful. Regarding your earlier comment, blocking access to arbitrary .php files is definitely a reasonable thing to do... but usually when that's done there's a whitelist of particular .php files that are allowed to be accessed. So I would think the root-level Drupal index.php can/should be on that whitelist?

omega8cc’s picture

@David_Rothstein -- I have asked those who reported the problem to provide more details on the paths/URIs/forms affected, because I believe they experienced the problem only on some forms -- otherwise we could easily reproduce the problem using the same D7 core with Commerce Kickstart, but we couldn't. Instead we have seen similar ajax pop-up errors, but not breaking anything, but it is probably separate issue.

As for blocking the direct access to /index.php by making it an "internal" location in Nginx, so it can be only accessed in the chain of internal requests, so effectively with aggressively forced clean URLs, we have tried that in the past, and I think the only reason was related to "security through obscurity" -- just to make it harder for bots to detect that the site is powered by PHP. But it broke things so we have removed the "internal" directive for /index.php because it doesn't provide anything important, besides some fancy "hardening".

cafuego’s picture

cafuego -- could you share more details on the exact URI path where #42 helps?

@omega8cc -- The specific path our developers reported was on a username autocomplete field in a custom admin view: /?q=admin/views/ajax/autocomplete/user/%.

Anonymous’s picture

@David_Rothstein: If there is a reason to whitelist files, i would do so even if i don't like it. But with your provided hook, my Drupal sites are running fine again as it was for years without access to index.php.

@omega8cc: I don't do it for fancy hardening and it is pretty simple for anyone to figure out that a site is powered by Drupal. Most obvious thing are "sites/default/files" links. The reason i prevent "index.php?q=whatever" requests openly is because i don't want to have them as much as i don't want to have "/node/1" or "/nodes" openly. Why do people come up with modules like Global Redirect?

omega8cc’s picture

@cafuego - Thank you, I will try this again.

@sint - Blocking direct access to /index.php (note, we are not talking about other php files here) has nothing to do with security, because there is no security difference / improvement between "index.php?q=whatever", "?q=whatever" and "whatever", other than URI elegance. You can achieve this elegance via rewrites/redirects as we do in BOA without making /index.php an "internal" location. You can filter anything you want before the request hits /index.php location, so making it internal does nothing useful. We have tried that config idiosyncrasies, but it only caused problems with zero benefit. There is just no point in making /index.php "internal" in Nginx.

cafuego’s picture

@omega8cc - I should note that we did end up using error code 418 for the autocomplete redirect, as 405 was already used to deny access in case of non-GET/POST request methods.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

gopisathya’s picture

#49 solves our problem for the time being.

vadym.kononenko’s picture

#49 works for me. Thank you.

cilefen’s picture

Title: Autocomplete function fails when index.html exist - After upgrading to Drupal 7.39 and chaos tools 1.8 » Autocomplete function fails when index.html exists - After upgrading to Drupal 7.39 and chaos tools 1.8
thulenb’s picture

I am not much of a coder and do not know how to apply patches. Is this issue going to be solved with the next drupal core update?

norwegian.blue’s picture

Using D7.43 & perusio 's nginx config I ran into this issue.
Tried #49 but I must have got the module wrong somehow as it broke.
#30 was a simple successful solution.
Is there any reason why one is better than the other?

mustanggb’s picture

@nzpling:

#30 Exposes that your site is running on PHP to the outside world and allows the possibility of your site being accessed(/search engine indexed) with unclean URLs.

#49 Essentially undoes this undesirable change internally without hacking core and results in no changes as far as the outside world is concerned.

Depends on your preferences, personally I find #30 unacceptable, but it doesn't look like David_Rothstein is willing to revert this change to core behaviour, so take your pick.

thulenb’s picture

So is the only way to get autocomplete funcionality run again to apply one of those patches? Isn't this functionality a very essential one and shouldn't it get fixed in Drupal Core?

David_Rothstein’s picture

Tried #49 but I must have got the module wrong somehow as it broke.

In what specific way did it break for you?

#30 Exposes that your site is running on PHP to the outside world and allows the possibility of your site being accessed(/search engine indexed) with unclean URLs.

I don't think it does either of those things:

  • Since everyone knows that Drupal runs on PHP (and it's easy to tell that a site runs Drupal) that info is already "exposed".
  • My understanding of #30 is that it opens up access to example.com/index.php?q=some/path, but that example.com/?q=some/path (which also has unclean URLs) would have already been allowed anyway. (Or if not, then you have bigger problems which date back before this issue.)

And again, if you really don't want "index.php" (or anything else you find undesirable) to appear in URLs, that should be easy to solve at the Nginx level by rewriting/redirecting the URLs rather than blocking them and that shouldn't interfere with autocomplete.

So is the only way to get autocomplete funcionality run again to apply one of those patches? Isn't this functionality a very essential one and shouldn't it get fixed in Drupal Core?

The best way is to fix the webserver to stop blocking URLs that Drupal relies on. If you don't run the webserver yourself, you could point whoever runs the server (or manages the Nginx configuration) to this issue.

If someone can come up with a reason why these Nginx configurations that block index.php are actually needed, or alternatively a way that Drupal core can work around the problem without reintroducing the problem this issue originally fixed or the security issue fixed in Drupal 7.39, I'm certainly willing to consider the idea of fixing this in core... But so far I don't think anyone has done that.

mustanggb’s picture

a way that Drupal core can work around the problem without reintroducing the security issue fixed in Drupal 7.39

And where can the issue/patch that the security issue fixed be found, which vulnerability even was it?

David_Rothstein’s picture

Unfortunately since it's a security issue there's no public issue it was discussed in, but it's described in the "Cross-site Scripting - Autocomplete system" section of https://www.drupal.org/SA-CORE-2015-003, and the code can be found within http://cgit.drupalcode.org/drupal/commit/?h=7.x&id=be00a1ced4104d84df2f3... (basically the form_process_autocomplete() and misc/autocomplete.js parts of it).

Roughly speaking, without going into too much detail, the problem is described in this comment in form_process_autocomplete():

  // Force autocomplete to use non-clean URLs since this protects against the
  // browser interpreting the path plus search string as an actual file.

Basically before this change, by having user-supplied data appended to the end of a URL which Drupal made an Ajax request to, it was possible for a malicious user to trick the browser into making Ajax requests to a malicious file.

Switching to non-clean URLs was the most backwards-compatible way possible to get the user-supplied data into a query string instead (which protects against the browser being fooled).

mustanggb’s picture

I can't think why stripping the malicious pattern in autocomplete.js wouldn't be sufficient on it's own.

However if it's not would it not work to do something like:

  $.ajax({
    ...
-   url: db.uri + '/' + Drupal.encodePath(searchString),
+   url: db.uri,
+   data: { search: Drupal.encodePath(searchString) },
    ...
  });

At any rate it's pretty hard to come up with ideas without any repro steps.

norwegian.blue’s picture

@David Rothstein
Called my module nginx_autocomplete_fix . (I'm a site builder more than developer, so haven't written modules from scratch before)
It rendered the content of the .module immediately below the body tag.:
/** * Implements hook_url_outbound_alter(). */ function nginx_autocomplete_fix_url_outbound_alter(&$path, &$options, $original_path) { if (isset($options['script']) && $options['script'] == 'index.php') { $options['script'] = ''; } }

Of course it didn't fix the issue.

plazik’s picture

So is the only way to get autocomplete funcionality run again to apply one of those patches?

You can add this code from #42 to drupal.conf.

###
### Rewrite legacy requests with /index.php to extension-free URL.
###
if ( $args ~* "^q=(?<query_value>.*)" ) {
  rewrite ^/index.php$ $scheme://$host/?q=$query_value? permanent;
}
David_Rothstein’s picture

I can't think why stripping the malicious pattern in autocomplete.js wouldn't be sufficient on it's own.

Possibly, but I think what's in autocomplete.js is there more as a backup. I'm not sure it's possible to catch every file traversal method with that regular expression.

However if it's not would it not work to do something like:

  $.ajax({
    ...
-   url: db.uri + '/' + Drupal.encodePath(searchString),
+   url: db.uri,
+   data: { search: Drupal.encodePath(searchString) },
    ...
  });

I think that would work but it would break essentially all autocomplete callbacks in core+contrib+custom modules and require them to be written differently, since they currently assume the data comes in via the menu system (e.g. it comes in via function parameters to https://api.drupal.org/api/drupal/modules!taxonomy!taxonomy.pages.inc/fu...).

One thing that maybe would work would be to make autocomplete.js verify URLs using a similar technique as ajax.js does (the latter was introduced in the same Drupal 7.39 security release). Then the request would no longer need to be made using unclean URLs since it is being verified via other methods instead. But that would be pretty involved.

David_Rothstein’s picture

@David Rothstein
Called my module nginx_autocomplete_fix . (I'm a site builder more than developer, so haven't written modules from scratch before)
It rendered the content of the .module immediately below the body tag.

Just a guess, but maybe you forgot to put a <?php tag at the top of the .module file? (That could cause the server to not know it's PHP code, so maybe that's why it printed it to the page instead.)

norwegian.blue’s picture

@David_Rothstein
Doh !
Thanks. That was of course it!
I now have two working solutions.

cherner’s picture

Is anyone still having this issue?

I still get a 403 error for the autocomplete, I'm on 7.43 and using apache.

pounard’s picture

I could put something in the release notes about this (or we could consider rolling it back in favor of a different solution) but I'm trying to understand the issue first... what is the use case for blocking direct access to Drupal's index.php file in the first place? That seems pretty unusual to me. Is it part of an attempt at denial-of-service protection at the Nginx level?

@David_Rothstein We actually have no Drupal sites with index.php public, autocomplete is now broken on all of them. We never did have any problems running any site where q= is disallowed.

Our sysadmins completely disabled the non-clean URL because they are very difficult to parse and validate, leaving sites opened to all kinds of security problems, I don't know everything into details, but for example Apache would leave no way of correctly validating or cleaning the q= parameter, because using URL encoding within the parameter leaves the possibility to write the same thing an infinite different ways.

cherner’s picture

I actually found that "...q=" queries were being revoked in the apache config.

I removed this restriction, is it a huge security threat to allow access to index.php? Why does autocomplete now force non-clean URLs? I wonder if that will be changed back. The users need autocomplete to work to manage the content.

pounard’s picture

Status: Closed (fixed) » Needs review
StatusFileSize
new2.35 KB

We actually won't expose index.php too, this is too much a security threat said our sysadmins, so I have made a patch that make this security measure optional, but on per default. We have other means to prevent the browser to hit a real file from autocomplete using nginx or apache configuration, and will use it instead.

Problem with the autocomplete security improvement in Drupal 7.39 is that it does break lots of our Drupal installations, or expose them to other security threats.

Please note this is not perfect and I do need to speak about this with our security team, but I do think I did understood the security issue (not completely though because it's not explained anywhere). I'm open too any criticism and like to find a way to make this more flexible, and better documented.

Status: Needs review » Needs work

The last submitted patch, 80: autocomplete-security-optional-2599326-80.patch, failed testing.

pounard’s picture

Test failures are not related.

David_Rothstein’s picture

Allowing that to be bypassed sounds like it might be reasonable, but let's make a new issue for that instead. If it was broken by Drupal 7.39 then it really doesn't have much to do with this issue (since the patch here was committed in Drupal 7.42).

mustanggb’s picture

Status: Needs work » Closed (fixed)
pounard’s picture

Thank you, sounds fair to open a new issue.

fortis’s picture

seems that i should create new one core patch if i will have directories at the drupal root such as: system/ and system/ajax because they will break drupal ajax

Konstantin Komelin’s picture

@fortis You just reported a new core security issue ;) We are all in trouble

David_Rothstein’s picture

Yes, if you put directories inside the Drupal docroot which conflict with paths defined by Drupal, you will break your site. Creating a top-level directory called "admin" would be another fun one :) I don't see what it has to do with this issue, though.... this was just about putting something like index.html there, which does not conflict.

The reference to a possible security issue sounds like a joke (which went over my head)... but if you seriously think there is a security issue with that for some reason, please report it to the Drupal Security Team rather than discussing it publicly here.

Tommy_001’s picture

#49 fixed the autocompletion problem on a nginx server. Thanks!

lamp5’s picture

Confirm!
#49 fixed the autocompletion problem on a nginx server. Thanks!

W.M.’s picture

#30 works for me with some little modifications that meet my fastcgi settings.

maxplus’s picture

Hi,
Thanks!

#49 also solved my issue on my local MAC OSX dev environment (based on laravel/valet)

Chris Charlton’s picture

This ticket is marked as closed but the dialog here has been people trying a patch for D7. Did I miss something there?