Problem / Motivation
Screen recording: https://drive.google.com/file/d/1Psq-aeG_j7XH3BTj_jl2eMz8ekb32vqy/view?u.... Browser: Firefox.
Originally reported as a security issue by David Rothstein, but making it into a public issue as the security team considers this non-critical and thus can be fixed in the public queue.
=====
Testing with Firefox, if you log out of a Drupal site and then hit the back button, you can see pages from the authenticated user's previous session.
This could be a problem on public computers, if the authenticated user had permission to see content protected by node access (or similar).
Variations of this have been reported in the public issue queue in many places. For example:
- #1859900: It is possible to view the contents of the authenticated pages by fetching the page from the browser cache memory, history.
- #1197544: History Back Button display webpage as it was seen by logged in User
- #197786: Some servers / browsers will cache pages even when header Cache-control: no-cache is set
We might say it's OK to discuss in public (and it certainly would be hard to find and unpublish all the existing issues now anyway) but I thought it might be useful to discuss here first.
As far as I can tell, the simplest way to fix this is to add the "no-store" header to all pages viewed by an authenticated user; however, Drupal used to do this but it was removed a long time ago (see http://drupal.org/node/109941) because it was causing all sorts of annoyances when an authenticated user tried to use the back button while still logged in. So, it's possible there isn't really a great solution here.
Security Note
Back and refresh attack
Screen recording: https://drive.google.com/file/d/1Psq-aeG_j7XH3BTj_jl2eMz8ekb32vqy/view?u.... Browser: Firefox.
There is a potential security concern here: imagine a user on a public computer. They log in to Drupal, go to pages that only an authenticated user should see, then logout. Unless the browser session was closed (the in-browser caches cleared), then someone else could come to that computer and click the back button to see those pages.
While the risk applies to anything you can see on the screen, it can also apply to anything in markup, notably a password field. See https://resources.infosecinstitute.com/browser-based-vulnerabilities-in-... for a generic description of this approach. The example would be someone attempts to login, it fails, and their password is still in the markup. In my testing, I did not see this issue with Drupal 8 because on a failed password attempt the password is cleared out (i.e. its not sent back to the browser). But, the principle of this approach could apply to other fields. If webforms module is used to submit important information, this issue could apply.
On Firefox, you can close the tab or close the browser (quit the browser application), then open the application again and be able to go back in history. In this scenario, the browser's setting is to "Open up previous tabs".
The user can visit multiple previous pages, not just the most recent.
Browser disk cache
Separately, but related, there is a concern about the browser storing these caches on disk. If those caches are not cleaned up, then anyone with access to that disk can access those cached pages.
Examples of this Attack
- April 2, 2020: Twitter Direct Message Caching and Firefox (see also Twitter's post)
- July 12, 2017: A report from ISE showed 21 of 30 sites tested were vulnerable to this type of attach (Note: the sites were not exclusively Drupal)
Steps to reproduce
1. Login to Drupal
2. Go to a page
3. Logout
4. Note you are redirected to the home page
5. Click the browser's "Back" button
6. Note you are now at the page from Step 2 and you see the Drupal admin toolbar (which should only show if you're authenticated)
This issue can be observed in Chrome and Firefox. With Chrome, if you disable cache using browser tools the issue is not observed. With Firefox, if you disable cache using browser tools the issue is still observed.
The issue persists even if you close the tab or quit the browser (tested in Firefox).
The user can click "Back" multiple times to see even older cached pages.
Proposed Resolution
Allow the user to use the back button, but change its behavior based on authenticated status. There are two basic scenarios:
Scenario 1: Logged out to logged in
1. Go to a page
2. Login to Drupal
3. Click the browser's "Back" button
4. Go to the page from Step 1, but you now see the "logged in" version of the page
Scenario 2: Logged in to logged out
1. Login to Drupal
2. Go to a page
3. Logout
4. Click the browser's "Back" button
5. Go to the page from Step 2, but you now see the "logged out" version of the page
The details on how to achieve this are TBD.
Potential Approaches
Here are some approaches discussed, it's uncertain which of these work. Please update this ticket to verify which do.
Option 1: JavaScript History API
See https://www.drupal.org/project/logout_redirect
Option 2: JavaScript window.onpageshow
See https://gomakethings.com/fixing-safaris-back-button-browser-cache-issue-...
Option 3: Cache-Control no-store
See #3130912: Incorrect Cache-Control headers for authenticated users
Option 4: Clear-Site-Data: "cache"
See https://www.fastly.com/blog/clearing-cache-browser
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
Reference Material
- https://madhatted.com/2013/6/16/you-do-not-understand-browser-history
- https://www.f5.com/services/resources/white-papers/caching-behavior-of-w...
- https://stackoverflow.com/questions/866822/why-both-no-cache-and-no-stor...
- http://blog.httpwatch.com/2008/10/15/two-important-differences-between-f...
- https://www.fastly.com/blog/clearing-cache-browser
- https://sookocheff.com/post/api/effective-caching/
- https://resources.infosecinstitute.com/browser-based-vulnerabilities-in-...
Comment | File | Size | Author |
---|
Issue fork drupal-1912514
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #0.0
dokumori CreditAttribution: dokumori commentedupdated the description slightly
Comment #1
gregglesAdding a related issue to fix this in paranoia.module.
Comment #2
matt2000 CreditAttribution: matt2000 commentedAnother possible way to mitigate this, at least in my testing with Chrome, may be to use the Vary:Cookie header, even for authenticated users. The private data may still be stored somewhere by Chrome, but at least a simple Back button click doesn't reveal it.
Comment #3
pwolanin CreditAttribution: pwolanin as a volunteer commentedDon't we already send
vary: cookie
? It looks like D7 always does unless you suppress it (e.g. for a CDN).Drupal 8 seems to have similar logic.
Comment #9
Hardik_Patel_12 CreditAttribution: Hardik_Patel_12 commentedFor Drupal 8 you can follow this link "https://www.drupal.org/project/logout_redirect" to solve this issue.
Comment #11
klausiI experimented a bit with the "no-store" cache control header. If that is set as default header for logged-in users then form field entries are lost when you click away by accident and use the back button. I think that would be a UI regression? We deliberately introduced the store option (now a default in browsers I assume) in #109941: Store site view in HTTP headers many years ago so that users do not lose context when they navigate back.
Comment #14
pwolanin CreditAttribution: pwolanin as a volunteer commentedWonder if this could be solved by having the user land on a page after logout that loads with no-store, then redirects?
Or maybe we could also/instead use JS history API to replace the entries for the current window?
https://developer.mozilla.org/en-US/docs/Web/API/History_API
Not sure either of these are bullet-proof in terms of eliminating authenticated content being stored by the browser.
However, there might be an even better option, a response header to ask the browser to clear cache:
https://www.fastly.com/blog/clearing-cache-browser
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
Seems to have reasonably broad browser support now.
Comment #15
alexpottAdding a
Clear-Site-Data: "cache"
cache on logout would do the trick. But won't people be upset that now their browser has to download all the assets which are common between the logged in sites and logged out site?Anyhow I had a quick test of the header and it worked great on Chrome but not in Firefox?!?! :( I tried both with and without quotes and with cache and *. Without quotes worked on chrome - it never worked on firefox. :(
Comment #16
nandeesh95 CreditAttribution: nandeesh95 at Trigyn Technologies Ltd commentedComment #17
nandeesh95 CreditAttribution: nandeesh95 at Trigyn Technologies Ltd commentedComment #18
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedThis issue was reported on a site build I'm working on. After lots of searching and trying to find the right search terms, I found this issue! It seems to describe what I'm seeing.
I'll add some reference material to the description of the ticket.
The comment from #14 is insightful. I see #3130912: Incorrect Cache-Control headers for authenticated users is planned to implement the "no-store" flag in the Cache-Control headers, I don't know if that will affect this ticket. I also see https://www.drupal.org/project/logout_redirect.
I'm going to document these various approaches in the description.
Comment #19
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedI'm going to have a go at describing the proposed resolution so that it's clear what this ticket is trying to achieve. Please revise as anyone sees fit.
Comment #20
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedComment #21
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedAdding a screen recording of the issue.
Comment #22
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedComment #23
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedSorry for all the updates, but I am going down the rabbit hole and finding important information.
Comment #24
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedIt seems there are two objectives, one functional and one security related.
Functional objective: ensure a user who is browsing along and hits the back button doesn't see an incorrect logged-in state. That is simply confusing.
Security objective: ensure that Drupal sites with sensitive information are not vulnerable to the two attacks described.
It seems like with some research and testing, an approach could be defined to address those objectives. However, it gives me concern as I don't know what indirect impacts are involved with various approaches.
Maybe we could focus on the security issue first? It seems like Drupal should have a default of Cache-Control: no-store for authenticated traffic, which is covered by #3130912. I will need to do some testing, but in the research I've done that seems to be what others are pointing to as the solution. I'm wondering if a ticket should be opened with webforms to have some flag on every webform that declares whether its a sensitive form, and if it is send the no-store header.
Comment #25
josephdpurcell CreditAttribution: josephdpurcell at Bounteous commentedI did a test of patch #5 on #3130912 and found it to meet the security objective of not exposing authenticated information through the back button attack. But, it did not meet the functional objective of an authenticated user clicking the "Back" button and seeing an anonymous page. See details here: https://www.drupal.org/project/drupal/issues/3130912#comment-13767062
Comment #26
drummWe’ve had a bit of an uptick in security researchers reporting this issue about Drupal.org.
Comment #27
pwolanin CreditAttribution: pwolanin as a volunteer commentedLooks like Clear-Site-Data header now is supported by pretty much all browsers other than safari
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
Comment #33
DieterHolvoet CreditAttribution: DieterHolvoet at Minsky commentedI wouldn't say that,
Clear-Site-Data
is not supported on Firefox and Safari. It was supported on Firefox at some point, but later moved to a feature flag because the implementation turned out to be incomplete/problematic. There doesn't seem to be a lot going on in the issue, so I'm afraid we'll have to wait a little longer before being able to use this feature.I tested the patch on Google Chrome, the header is successfully added to the logout response but it looks like I'm still able to access cached admin pages by using the back button. Am I doing something wrong?
Comment #34
DieterHolvoet CreditAttribution: DieterHolvoet at Minsky commentedComment #35
Rakhi Soni CreditAttribution: Rakhi Soni at Smashing Infolabs Pvt. Ltd. for Smashing Infolabs Pvt. Ltd. commentedAttached rerolled patch against 9.4x,,
Kindly review patch,,
Comment #36
DieterHolvoet CreditAttribution: DieterHolvoet at Minsky commentedA MR was opened, so no need for a rebase. More work is still needed so setting back to Needs work.
Comment #37
bnjmnmComment #39
znerol CreditAttribution: znerol commentedOWASP has a page in the WSTG about this topic. Their recommendation:
This is basically what Drupal is doing already. Except that there are some additional directives on authenticated pages (
Cache-Control: must-revalidate, no-cache, private
). I wouldn't expect that the extra directivesno-cache
andprivate
would reverse the effect ofmust-revalidate
. So I looked at other response headers relevant to browser caching.Turns out that we are missing
Vary: Cookie
on authenticated responses. The culprit isFinishResponseSubscriber::setResponseNotCacheable()
. The comment in this method says:There is only one situation where this behavior would be correct: On a selected route which is delivered with
Cache-Control: no-store
in all cases (independently whether a session is open or not).In all other situations, the
Vary: Cookie
header must be on every response. No matter whether a user is logged in or not or a session is open or not.Comment #40
znerol CreditAttribution: znerol commentedComment #44
znerol CreditAttribution: znerol commentedMR !3505 adds
Vary: Cookie
to every response. Obviously needs manual browser testing.Comment #45
znerol CreditAttribution: znerol commentedMR !3505 doesn't seem to fix the problem. Interestingly this wasn't an issue on the previous build of the site I am mainly working on. I get a 403 (i.e., the expected behavior) with the following testing scenario in Firefox:
The legacy site was based on Drupal 7 and Authcache. Authcache encourages the browser to cache pages even for authenticated users. However, it also ensures that the ETag is different for each variant of a page.
I think that when Authcache is active, the browser is replacing the cached frontpage in step 2 (after login), and the cache is replaced again in step 4 (after logout). My hunch is that a cache replacement (in contrast to a cache insert) invalidates previous history and as a result the browser attempts to refetch the user page when the back button is hit.
Pure speculation though, more testing is clearly necessary. Also I am a bit disappointed by the OWASP page linked in #39. It doesn't seem to cover the full problem.
Comment #47
Pemson18see https://www.drupal.org/project/drupal/issues/3130912
Comment #48
sokru CreditAttribution: sokru as a volunteer commentedShould we consider Option 3:
Cache-Control: no-store
? On linked issue #3130912: Incorrect Cache-Control headers for authenticated users there's patches to implement it with test coverage. OWASP recommends the approach also on its FAQ page https://owasp.org/www-community/OWASP_Application_Security_FAQ#how-do-i-...Twitter.com and Google seems to rely on Cache-Control: no-store header on authenticated services.
Fastly article from 2014 states about
Vary: Cookie
: "Cookie is probably one of the most unique request headers, and is therefore very bad."Comment #49
sokru CreditAttribution: sokru as a volunteer commentedI tested @znerol's MR !3505 with DDEV, it works only with HTTPS enabled, like mentioned on OWASP documentation. Tested with Firefox, Chrome, Edge. Unfortunately on Safari the back button showed the cached content.
Wrote on #3130912: Incorrect Cache-Control headers for authenticated users that we should implement the Cache Control header change on that issue. It will partially fix this issue. On Safari there's a bug https://discussions.apple.com/thread/251817133 and I hope we could narrow down this issue just for Safari.