I suspect you have seen the many discussions in the forums about problems that users are having with logging in and logging out in Drupal 4.7. I just wanted to provide you with additional information and perspectives on this issue. I also want to urge the team on to address the issue very soon. It may not be perceived this way, but this problem is, in the end, a major security issue, as I'll explain in a moment.

We are running a very basic install of Drupal that is still under development. We have only a limited number of additional modules installed, and only a few users accessing perhaps 20-30 nodes of content.

Yet was are facing the following problem. When users go to log in to our site, their first attempt apparently fails. However, if they then re-enter their username and password, they will be "in" the site.

Later, when they want to log out, they will choose the log out menu option and be sent to the login page which is displayed to users who have not yet logged in.

Here is the security issue: In reality, the two logins that were necessary created two instances of the user. When the user logs off, she has only logged off one instance of her logins. The second instance is still logged in. (This is all readily apparent from the watchdog logs.)

Not surprisingly, if the user closers her browser, but later re-opens and points it to our web site, amazingly (and startlingly), she's still logged in!

If she logs herself off this time, she is truly logged off. The next time she returns to the site, she will have to log in before she is granted access to the main content of the site.

I assume you see the security issue this raises.

And, as you know, many people are having this issue. Here are just a few of the discussions that have been on the forums:

http://drupal1.osuosl.org/node/55364
http://drupal.org/node/65659

Again, this is just a small sample of the large number of discussions of this issue. It seems to be very common.

Allow me to give you some details of our particular situation which may help you isolate the source of the problem.

- We are running Drupal on a shared server at www.site5.com. It is running using PHP 5.
- We are using the very latest version of user.module (1.630).
- We have only a very limited number of relatively basic contributed modules installed; ours is not a particularly fancy site at this point.
- We use TinyMCE for content entry and editing.
- The "login" page which we present to anonymous users is the "access denied" page; anonymous users see this because their "role" does not permit them to see any content or do anything; thus, they get the "access denied" page.
- Our site makes use of two menus: the standard navigation menu, and a separate menu which is visible only to users in a certain role; we control visibility of the extra menu using the block module's ability to suppress visibility if a PHP snippet returns a TRUE value.
- We use the "sections" module to have the "access denied" page use a slightly different theme than the other parts of the website (because it is displayed only in those instances when an anonymous user is viewing the page); both the themes we use, however, are just slightly modified versions of the Pushbutton theme.
- We experimented once with an access control module (basic_access?) which created some problems when we attempted to uninstall it; in the end, we had to use the solution suggested here (http://drupal.org/node/64114) to reset all access control on the site; we now have no access control modules installed or running; at this point, we only use roles for access "control" (sometimes combined with PHP as with the blocks module control of menu visibility, as I mentioned earlier).

We have experimented with some of the patches (to user.module) and other potential solutions that others have offered for our problem. They have not worked. As a minor example, we tested whether deleting browser cookies before an anonymous user submits his username and password would ensure a successful first login attempt (as was suggested by one forum poster). This did not work.

I think that is all the background information I have for you regarding the problem. Feel free to contact me, however, if you have any follow-up questions.

Thank you for your time ... and good luck!

CommentFileSizeAuthor
#2 DualLoginHeaders.txt36.53 KBbsimple

Comments

killes@www.drop.org’s picture

can you use the firefox live http headers extension to capture the http headers sent back and for this process?

bsimple’s picture

StatusFileSize
new36.53 KB

Sure.

I'm attaching a file with the header exchange for a dual login. I turned on logging right before I went to the site (www.example.com), and kept it on as I attempted the first login, and then turned logging off after the successful second login. (The first login, as generally is the case, returned me to the login prompt.)

I don't include the header exchange for that sequence here, but I then logged off from the site and closed the browser. I then re-opened the browser, type in www.example.com, and instead of ending up at the anonymous user login, ended up inside the site as a logged in user (the one I had dual-logged in as). I then logged out a second time.

One additional comment which could be helpful. If one ends up "inside" the site by typing www.example.com and unexpectedly ending up logged in -- as a result of only a single log off having been executed before the browser was closed -- there are certain actions that will "kick you out" of the site and send you the login prompt. The one action that specifically sticks in my head in this regard is trying to edit a page.

Hope this helps.

Note: If you feel there is any confidential / private information in the header exchange log file, please download it for your use and then have it detached from this message. I have edited the log file a bit to anonymize it, but I may not have deleted all private information.

morbus iff’s picture

bsimple: I'm only going to address your liveheaders, because that's the only thing concrete, quote unquote. In this particular case, there are three sessions and cookies getting created. Before I go on - you say that, sometimes, when you login to the site, you'll login and get redirected back to a page where the username and password box are still there? Next time that happens, without doing anything else, hold down the Shift key and click the Refresh button on your browser. I suspect that your browser is loading the cached ("I'm not logged in") version of the page you logged in from and, when Drupal redirects you back to that location, your browser dutifully loads the cached version, making it appear you're not logged in. A shift-refresh will force a real retrieval of the page. I've had this happen to me before, and a shift-refresh always shows me that I'm properly logged in.

Now, your liveheaders. First off, note the very first line "http://www.example.com". This starts our first series of requests, with the PHP sessionID of 3fd8053e826998653f35324a0ff725bc, which the browser accepts and starts sending with future requests (as seen in the Set-Cookie/Cookie lines of the request/response). The first problem occurs when the user logins - look for "POST /?destination=node%2F4 HTTP/1.1". The user is logging in under "http://www.example.com", but the site redirects the user to "http://example.com/node" (the Location: line). This tells me that your settings.php has "example.com" (no-www) as a base_url. To a browser and, more importantly, to sessions and cookies, "http://www.example.com/" and "http://example.com/" are NOT the same thing. Logging in under example.com will NOT allow you to use www.example.com, and vice-versa. And, if you auth under example.com, then later auth under www.example.com, that will log you out of the example.com session (I *think*).

While I have not tried it myself, I've heard whisperings that something in 4.7 is supposed to make this "better" somehow, simply by NOT setting the base_url in your settings.php (so that Drupal determines the base_url based on the client's request, not the admin's wishes). I'm not sure exactly what it does, but it's on my todo list to try on disobey.com (which is also hosted at Site5).

With all that said, it gets a little more confusing. The next time the session/cookie changes is again with a "POST /?destination=node%2F4 HTTP/1.1" (where it changes to 9358ca6b8bff6f23a3d28b453b178423). As we can see in the payload (the line after "Content-Length"), the client is again logging in (Incidentally, you were right - you sanitized everything but the password of the user, which is now available in cleartext. You'll want to change the password.). Now, generally speaking, a session/cookie IS SUPPOSED to change when you go from an anonymous user (one state) to an authenticated user (another state). However, it is unclear to me why this is actually happening (ie. if we already logged in successfully on the first request, where is this second login coming from?). So, I go back and I look at the first login attempt (the first match for "POST /?destination=node%2F4 HTTP/1.1).

I notice two things: PHP is setting two cookies, the first being your anonymous (presumably) cookie and the second being an authenticated one (presumably): Set-Cookie: PHPSESSID=3fd8053e826998653f35324a0ff725bc; expires=Sun, 09 Jul 2006 13:02:33 GMT; path=/. Set-Cookie: PHPSESSID=f56521d9d7cdd94c8c9b26318f08e0b7; expires=Sun, 09 Jul 2006 13:02:33 GMT; path=/. And then a third cookie is actually set shortly thereafter (d0f6562482d6e9d3f3d31955d4ba1ee8). I think the key here is the fact that a server error happened somewhere - the only thing I can think of is that you entered the wrong password or something Bad Happened (right after the client's POST request, it issues a GET to /node which gives an Access Forbidden error, as evident by the server's HTTP/1.x 403 OK response).

So, long story short: some of it I can explain, and some of it I can't. The stuff I can't explain doesn't mesh up with reality (why a 403 response?). If you could, do the following for me: a) attempt to duplicate the "I login and then get sent to a page with the username/password block again" problem and b) shift-refresh the page when that happens, to see if the box goes away. Then c) clear all your cookies and cache and d) do everything all over again, again attaching your liveheaders, so that we have something of a comparison.

bsimple’s picture

Morbus: Many thanks for so carefully going through the header information.

Hats off ... I think you've isolated the problem. It is, indeed, the fact that some calls were being made to www.example.com, and others to simply example.com.

I have changed the $base_URL setting in settings.php to be www.example.com. With that change, all users who arrive at www.example.com and attempt to login are immediately successfully logged in. Dual logins are not necessary. Likewise, when those users log out of their sessions, they are completely logged out. Shutting down the browser after logout, starting it up again, and entering www.example.com will bring them to the login page (access denied page); they will not seem to still be logged in.

This is reflected in the watchdog logs, as well. Each successful login only leads to one login entry in the log. When dual logins were required, the log entries showed two successful logins, even though only the second login actually permitted entry into the site.

Looking closer at what was going on when two logins were required, it's easy to see what exactly happened. The first login attempt from www.example.com successfully got the user logged into Drupal, but the difference between www.example.com and example.com (the setting in $base_url) kicked the user back to the login page (which is also the "access denied" page). BUT, at this point, the login page was not *exactly* the same as the first login page, since it was at example.com (*not* www.example.com).

When the user tried a second time to login, Drupal once again accepted the login, but now there was a match between the URL from which the user was coming and $base_url, so the user got into the site.

By the way, just as an FYI ... doing a shift-refresh after the first login attempt (before I made changes to the settings.php file) did not result in my being logged in. The shift-refresh just led to a redisplay of the login page.

I think that, from my perspective, this issue has been dealt with. I will leave it to those responsible for the Drupal core to decide whether the matter should be closed, or whether it requires a patch. Personally, I think Drupal should be more robust -- i.e., that it shouldn't lead to a situation where an instance of "www" leads to a potential security breach. But others may feel differently.

One last piece of information which may influence whether this discussion leads to a closer look at Drupal's code ... Even though the dual login (dual logout) probably has been eliminated by "fixing" the settings.php file, there's still one thing that unsettles me a bit. If you look at watchdog's logs, every successful user login is preceeded by two "denied access" messages. I can understand there being one such message; after all, the "access denied" is what lands the user at the "login" page. But I don't understand the second one.

Again, thank you, Morbus, for your thorough review of this issue. Let me know if I can be of any further assistance. I certainly hope that what we've found here will help others.

morbus iff’s picture

bsimple: good to hear. One thing I'd like you to try is, and this will only work if you're using 4.7, to comment out the base_url in settings.php entirely and try your tests again. I'm curious to see if, and how, the double login stuff works. And, yes, as you've suggested, this is a widely reported problem, and we have taken steps to make this an admin choice in 4.7. The first one is what I want you to test - leaving the base_url commented (preceded by a # or a //, your choice). The second is actually included in the documentation for base_url:

* You might also want to force users to use a given domain.
* See the .htaccess file for more information.

See, the way you've fixed it, the problem now exists in reverse - if someone typed in JUST example.com, they'd login, get redirected to www.example.com, and the double login problem rears it's ugly head again, with the same security ramifications. To *force* your users to *always* go to www., you have to do it at a higher level than Drupal - via the webserver. The .htaccess facilitates this and requires Apache's mod_rewrite. A (partial) spit from that file:

# If your site can be accessed both with and without the prefix www. you
# can use one of the following settings to force user to use only one option:
#
# If you want the site to be accessed WITH the www.
# only, adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
# RewriteRule .* http://www.example.com/ [L,R=301]

bsimple’s picture

Morbus: I've looked into the things you were curious about. Here's what I found.

First, yes, changing $base_url to www.example.com created the same problem we had before, but in reverse. We tested for this as soon as we changed $base_url to www.example.com, and, sure enough, the problem happened as consistently as it did before, just under the opposite circumstances..

Still, we stuck with the new $base_url setting because we felt it was more likely that users would be accustomed to typing "www" before the domain name, than to typing the domain name alone. Even more importantly, we knew there was going to be some way that we were going to redirect users from one domain name option to the other to avoid the problem entirely.

Second, commenting out the $base_url assignment in settings.php also gets rid of the multiple login problem. When the assignment is commented out, users can type in example.com or www.example.com before they login, and they will only have to submit their username and password once to gain access to the site.

Finally, uncommenting the .htaccess lines you mentioned creates the redirects necessary to change example.com to www.example.com, as desired. However, as best I can tell, the rewrite rule redirects *every* URL involving example.com -- e.g., example.com/admin/logs, or example.com/user/1 -- to www.example.com. Isn't this a bit extreme? Wouldn't it be better to rewrite example.com/admin/logs as www.example.com/admin/logs?

An alternative rewrite approach recommended to us by the Site5.com support staff was as follows:

RewriteCond %{HTTP_HOST} ^example.com$ [NC]
RewriteRule (.*) http://www.example.com/$1 [R,L]

One other question: Which approach to solving this problem is more "robust"? Commenting out the $base_url assignment, or using the .htaccess redirects? I'm inclined to opt for the redirect approach ... or a modified version of it. But I'm a little concerned about the server overhead it may create. What do you think?

I hope the feedback I've provided above is useful for those working on these issues. Thanks, once again, for your advice.

morbus iff’s picture

Status: Active » Closed (works as designed)

The only additional and "major" overhead caused by the .htaccess is when and if you are using mod_rewrite. And, based on your data, you ARE already using it (with Clean URLs) so adding that additional rule is not causing any overhead at all. Granted, things would be faster if this was done in the httpd.conf file (which I know you don't have access too), but we're looking at a really small measure here - milliseconds under thousands of hits a minute. I wouldn't worry about it.

From a technical standpoint, the safest best is probably leaving the base_url commented. That is the Future For settings.php, and you'll find that happening in a lot more places than you will .htaccess modification (which is often hidden to non-technical users, and is full of uberdygooberdy). It also presumes that a) you can use .htaccess files on your server and b) you can use mod_rewrite. Site5 naturally passes both those tests, but a lot of hosts don't (as evident by a simple Google search for Drupal sites not using clean URLs).

From an opinionated standpoint, I will *never* use a "no-www" to "www" redirect. I don't buy into this crazy fad about no-www being better for the web, design, and what have you. It's just crank pot theory. I prefer to allow my users to use my site with, or without, the www, so I'd never do a forcing. Now that you've done my work for me (I have been lazy about trying out the commented base_url for a bit now), I'll be commenting that out myself shortly to solve a similar problem for disobey.com (for me, I try to *always* link to www.disobey.com, but I nearly *always* type in my address as "disobey.com", so I get the dual login problem frequently enough too).

Finally, issue-wise, I think I'm going to close this under a "by design" status.

bsimple’s picture

Status: Closed (works as designed) » Active

One quick follow-up to the comments I just posted ...

I didn't explicitly say this in my previous note, but the alternative rewrite rule suggested by our hosting support staff works the way I expected such a rule to work. That is, it rewrites example.com/admin/logs to be www.example.com/admin/logs .

This is different from the suggested code in the Drupal-provided .htaccess file, which, when uncommented, sends all rewritten URLs to the site's main page, www.example.com.

morbus iff’s picture

Title: Multiple login / multiple logout required » Improve forced www/no-www .htaccess rule to pass along paths.
Version: 4.7.2 » x.y.z
Component: other » base system
Category: bug » feature

Leaving as active, updating issue title.

fool2’s picture

http://drupal.org/node/60584#comment-109134

Not only is this a security hole, but it can break logins because of cascading cookies.

Morbus, the anomalies you found in the first batch might also be caused by this cookie dominance... even if a user goes to www.site.com they'll still get the site.com session. session_regenerate_id() will change the session id in the database and also send a cookie for www.site.com, but when the user is forwarded to site.com, the site.com cookie session id will be used over the www.site.com session id. Once a user has both cookies, they MUST be logged in under site.com for their session to work anywhere. Even though www.site.com gets the cookie and can log in under that same session id, when the id is regenerated the new id will be sent to a www.site.com cookie. In this case, the www.site.com cookie-session can never be logged off, only regenerated (even though it will appear that www.site.com is logged off if the site.com session cookie is anonymous)

fool2’s picture

Category: feature » bug
Priority: Normal » Critical

setting to critical because having a site.com base url will mean any log in under www.site.com cookie cannot be logged out of.

By simply deleting the site.com cookie another user of the computer can gain access to the logged in www.site.com session cookie.

morbus iff’s picture

Category: bug » feature
Priority: Critical » Normal

If this concerns you, there are already two ways of solving the problem as described above. This report is now for a separate issue entirely.

morbus iff’s picture

Anyways, looks like the other issue you've linked to is more relevant/detailed.

fool2’s picture

Ok then. Well the feature here is really a solution to the issue for some people. +1 on the rewrite code bsimple provided in #6.

Sorry for the excitement and not thinking out the posting, I'd been up all night

LAsan’s picture

Version: x.y.z » 7.x-dev

Feature request moving to cvs.

grendzy’s picture

Status: Active » Closed (duplicate)