Delete Cookies for a Single Site

Sometimes a site will not load by default but it works just fine in InPrivate mode or when loaded in a different browser profile. In many such cases, this means there’s a bug in the website where they’ve set a cookie but fail to load when that cookie is sent back.

This might happen, for instance, if a site set a ton of cookies over time but the server has a request length limit. After the cookies build up through the course of normal browsing, the 16k header limit is exceeded and the server rejects all further requests by dropping the connection or showing a message like HTTP/431 Bad Request – The size of the request headers is too long, Bad Request – Request too long, or ERR_TOO_MANY_REDIRECTS. This problem is unfortunately common for large web properties with many sub-domains, like Microsoft.com.

Fortunately, it’s often easy to fix this problem in the new Edge (and Chrome).

Delete Cookies for the Current Site

On the error page, click the icon next to the address bar and see whether there are Cookies in use:

ClearSiteCookies1

If so, click the item to open the Cookies in use screen. In the box that appears, select each server name and click the Remove button at the bottom to remove the cookies set for that server:

ClearSiteCookies2

After you remove all of the cookies, click the Done button and try reloading the page.

Delete Non-Cookie Storage

If clearing cookies doesn’t improve things, it’s possible that the problematic information is stored somewhere else. Sometimes sites store data using a different technology (e.g. HTML5 Storage, indexedDB, etc). You might try deleting this storage too to see if it improves things.

In Chrome or Edge, navigate to chrome://settings/siteData and search for the site. Use the trash can icon next to the entries to clear the site’s data:

Then, try reloading the page.

-Eric

PS: If you have a server hitting this problem, use fewer/smaller cookies, and reconfigure your server to accept larger headers. Reconfiguration might be required to use your server if your server uses Active Directory authentication and your visitors have large AD tokens.

Securely Displaying URLs

One of my final projects on the Chrome team was writing an internal document outlining Best Practices for Secure URL Display. Yesterday, it got checked into the public Chromium repro, so if this is a topic that interests you, please have a look!

Additionally, at Enigma 2019, the Chrome team released Trickuri (pronounced “trickery”) a tool for manual testing of URL displays against tricky attacks.

Demystifying ClickOnce

Update: ClickOnce support is now available in modern Edge; see the end of this post.

As we rebuild Microsoft Edge atop the Chromium open-source platform, we are working through various scenarios that behave differently in the new browser. In most cases, such scenarios also worked differently between 2018’s Edge Legacy (aka “Spartan”) and Chrome, but users either weren’t aware of the difference (because they used Trident-derived browsers like IE inside their enterprise) or were aware and simply switched to a Microsoft-browser for certain tasks.

One example of a behavioral gap is related to running ClickOnce apps.

ClickOnce is a Microsoft application deployment framework that aims to allow installation of native-code applications from the web in (approximately) one click.

Chrome and Firefox can successfully install and launch ClickOnce’s .application files if the .application file specifies a deploymentProvider element with a codebase attribute (example). After download, the user double-clicks on the downloaded .application file in the Downloads folder and the install proceeds:

ClickOnce Install Prompt

However, it’s also possible to author and deploy an .application that doesn’t specify a deploymentProvider element (example). Such files will launch correctly from Internet Explorer and pre-Chromium Edge, but fail for downloads from Firefox and Chrome with an error message:

ApplicationCannotBeStarted
ClickOnce fails for a downloaded .application file.

So, what gives? Why does this scenario magically work in Edge Legacy but not Firefox or Chrome?

DirectInvoke

The secret can be found in the EditFlags for the Application.Manifest ProgId (to which the .application filename extension and application/x-ms-application MIME type are mapped).

ApplicationManifestRegistry
Registry settings for the Application.Manifest ProgId.

The EditFlags contain the FTA_AlwaysUseDirectInvoke flag, which is documented on MSDN as:

FTA_AlwaysUseDirectInvoke 0x00400000
Introduced in Windows 8. Ensures that the verbs for the file type are invoked with a URL instead of a downloaded version of the file. Use this flag only if you’ve registered the file type’s verb to support DirectInvoke through the SupportedProtocols or UseUrl registration.

I wrote more about DirectInvoke here; if you peek in the Application.Manifest‘s Shell\Open\Command value, you’ll find that it calls for running the ShOpenVerbApplication function inside dfshim.dll, passing along the .application file’s path or URL in a parameter (%1):

"C:\Windows\System32\rundll32.exe" "C:\Windows\System32\dfshim.dll",ShOpenVerbApplication %1

And therein lies the source of the behavioral difference.

When you download and open an Application.Manifest file from Edge Legacy, it passes the source URL for the .application to the handler. When you download the file in Firefox or Chrome, it passes the local file path of the downloaded .application file. With only the local file path, the ShOpenVerbApplication function doesn’t know how to resolve the relative references in the Application Manifest’s XML and the function bails out with the Cannot Start Application error message.

Setting FTA_AlwaysUseDirectInvoke also has the side-effect of removing the “Save” button from Edge Legacy’s download manager:

NoSave

…helping prevent the user from accidentally downloading an .application file that won’t work if opened outside of the browser from the Downloads folder (since the file’s original URL isn’t readily available to Windows Explorer).

Advice to Publishers

If you’re planning to distribute your ClickOnce application from a website, specify the URL in Visual Studio’s ClickOnce Publish Wizard:

Manifest
Specify “From a Web site” in the ClickOnce Publish Wizard.

This will ensure that even if DirectInvoke isn’t used (e.g. from Chrome or Firefox), the invocation of the ShOpenVerbApplication function can still find the files needed to install your application.

Workarounds for Firefox & Chrome

A company called Meta4 offers a Chrome browser extension that aims to add fuller support for ClickOnce to Chrome. The extension comes in two pieces– a traditional JavaScript extension and a trivial “native” executable (written in C#) that simply invokes the ShOpenVerbApplication call with the URL. The JavaScript extension launches and communicates with the native executable running outside of the Chrome sandbox using Native Messaging.

Unfortunately, the extension is a bit hacky– it installs a blocking onBeforeRequest handler which watches all requests (not just downloads), and if the target URL’s path component ends in .application, it invokes the native executable. Alas, it’s not really safe to make any assumptions about extensions in URLs (the web is based on MIME types, rather than filenames).

WARNING: DO NOT INSTALL “ClickOnce” extensions like this one into Microsoft Edge. The extension will break Edge’s built-in ClickOnce handling.

Edge’s ClickOnce Support

ClickOnce support was added to Edge 77+, and is now on-by default.

The feature was disabled-by-default prior to Edge 87, but could be enabled via edge://flags/#edge-click-once or Group Policy. ClickOnce on Windows 7 was not working correctly until Edge 91.

Note that the ClickOnce implementation in Edge will always1 prompt the user before the handler is invoked:

In Edge Legacy/IE, sites in your Intranet/Trusted Sites Zone could spawn the .application handler without any prompt from the browser. That’s because these older browsers respect the FTA_OpenIsSafe bit in the EditFlags for the application.manifest progid. The new Edge tries to limit its use of Windows Security Zones, and it thus does not support the FTA_OpenIsSafe bit.

-Eric

Appendix A: IE Implementation

Notably, Internet Explorer (and thus IE Mode) doesn’t rely upon the DirectInvoke mechanism for ClickOnce; removing the EditFlags value entirely causes IE to show an additional prompt, but the install still succeeds. That’s because IE activates the file using a MIME handler (see the CLSID subkey of Application.Manifest) much like it does for .ZIP files. The DirectInvoke mechanism was invented, in part, to replace the legacy MIME handler mechanism.

Appendix B: Launch-from-Edge Problems?

If you have a problem whereby each time you click “Open” on the ClickOnce prompt, the browser simply opens a new tab to ask the same question, this is caused by buggy security software. Edge handles ClickOnce by passing the URL into ShellExecuteEx, with the SEE_CLASS_MASK flag set and the hkeyClass pointed at the ASSOCKEY_SHELLEXECCLASS for the .application file extension:

  parameters.fMask = SEE_MASK_NOZONECHECKS | SEE_MASK_NOASYNC | SEE_MASK_CLASSKEY;
  std::wstring lpFile = base::UTF8ToWide(url.spec());
  parameters.lpFile = lpFile.c_str();
  parameters.hkeyClass = handler_class;
  parameters.nShow = SW_SHOWNORMAL;
  ShellExecuteExW(&parameters);

It appears that the security software’s thunk does not understand the significance of the SEE_MASK_CLASSKEY and it effectively strips it out. ShellExecuteEx, thus handed what is effectively a plain old HTTPS URL, then launches the default web browser passing the URL. We then end up with a new tab trying to invoke the ClickOnce URL, and the process repeats, creating a new tab each time you click the Open button. If your admin had set the policy to allow ClickOnce to automatically open without a prompt, I assume the browser will endlessly open tabs until your machine melts.

Appendix C: File URLs with non-ASCII characters

Recently, a user of Edge 120 noted that Edge doesn’t successfully launch ClickOnce applications from file:/// schemed URLs that contain non-English characters. The problem here is that Chromium %-escapes the non-English characters into UTF-8, such that a link to \\server\files\Testä.application is written as file://server/files/Test%C3%A4.application. Unfortunately, for historical reasons, Windows expects %-encoded octets in file URLs to be encoded as %-escaped into the client OS’ ANSI codepage, so for my US-English system, that would be file://server/files/Test%E4.application.

After the user clicks “Open” in Edge’s ClickOnce security prompt, they’ll see a dialog from Windows complaining that the file cannot be found:

To workaround this problem, serve your files from HTTPS, or avoid using non-ASCII characters in your application’s filename.


1 Not really always. Starting in Edge 93, you can use the AutoOpenFileTypes and AutoOpenAllowedForUrls policies to bypass prompts for both ClickOnce and DirectInvoke. If you configure .application files to automatically open, the ClickOnce prompt will be bypassed and ClickOnce will launch from sites of your choosing.

I think configuring Edge in this way is a bad tradeoff (the convenience of removing one click doesn’t seem worth an increase in attack surface), but it is supported.

Private Browsing Mode

Note: This blog post was originally written before the new Chromium-based Microsoft Edge was announced. As a consequence, it includes discussion of the behavior of the Legacy Microsoft Edge browser. The new Chromium-based Edge behaves largely the same way as Google Chrome.

Last Update: 13 June 2025

InPrivate Mode was introduced in Internet Explorer 8 with the goal of helping users improve their privacy against both local and remote threats. Safari introduced a privacy mode in 2005.

All leading browsers offer a “Private Mode” and they all behave in the same general ways.

Privacy Mode Behaviors

HTTP Caching

While in Private mode, browsers typically ignore any previously cached resources and cookies. Similarly, the Private mode browser does not preserve any cached resources beyond the end of the browser session. These features help prevent a revisited website from trivially identifying a returning user (e.g. if the user’s identity were cached in a cookie or JSON file on the client) and help prevent “traces” that might be seen by a later user of the device.

In Firefox’s and Chrome’s Private modes, a memory-backed cache container is used for the HTTP cache, and its memory is simply freed when the browser session ends. Unfortunately, WinINET never implemented a memory cache, so in Internet Explorer InPrivate sessions, data is cached in a special WinINET cache partition on disk which is “cleaned up” when the InPrivate session ends.

Because this cleanup process may be unreliable, in 2017, Edge Legacy made a change to simply disable the cache while running InPrivate, a design decision with significant impact on the browser’s network utilization and performance. For instance, consider the scenario of loading an image gallery that shows one large picture per page and clicking “Next” ten times:

InPrivateVsRegular

Because the gallery reuses some CSS, JavaScript, and images across pages, disabling the HTTP cache means that these resources must be re-downloaded on every navigation, resulting in 50 additional requests and a 118% increase in bytes downloaded for those eleven pages. Sites that reuse even more resources across pages will be more significantly impacted.

Another interesting quirk of Edge Legacy’s InPrivate implementation is that the browser will not download FavIcons while InPrivate. Surprisingly (and likely accidentally), the suppression of FavIcon downloads also occurs in any non-InPrivate windows so long as any InPrivate window is open on the system.

Web Platform Storage

Akin to the HTTP caching and cookie behaviors, browsers running in Private mode must restrict access to HTTP storage (e.g. HTML5 localStorage, ServiceWorker/CacheAPI, IndexedDB) to help prevent association/identification of the user and to avoid leaving traces behind locally. In some browsers and scenarios, storage mechanisms are simply set to an “ephemeral partition” while in others the DOM APIs providing access to storage are simply configured to return “Access Denied” errors.

You can explore the behavior of various storage mechanisms by loading this test page in Private mode and comparing to the behavior in non-Private mode.

Within IE and Edge Legacy’s InPrivate mode, localStorage uses an in-memory store that behaves exactly like the sessionStorage feature. This means that InPrivate’s storage is (incorrectly) not shared between tabs, even tabs in the same browser instance.

Update: Some browsers offer the option (opt-in or opt-out) to disable third-party cookies entirely while running in Private Mode, like this toggle on Chrome’s Incognito new tab page:

Similarly, Edge’s InPrivate mode offers a toggle to use the Strict Mode of the Tracking Prevention feature:

Network Features

Beyond the typical Web Storage scenarios, browser’s Private Modes should also undertake efforts to prevent association of users’ Private instance traffic with non-Private instance traffic. Impacted features here include anything that has a component that behaves “like a cookie” including TLS Session Tickets, TLS Resumption, HSTS directives, TCP Fast Open, Token Binding, ChannelID, and the like.

As of June 2024, Chromium-based browsers now default to enabling HTTPS First mode (automatic upgrades from HTTP to HTTPS URLs, if possible) in Private Mode.

Automatic Authentication

In Private mode, a browser’s AutoComplete features should be set to manual-fill mode to prevent a “NameTag” vulnerability, whereby a site can simply read an auto-filled username field to identify a returning user.

On Windows, most browsers support silent and automatic authentication using the current user’s Windows login credentials and either the NTLM and Kerberos schemes. Typically, browsers are only willing to automatically authenticate to sites on “the Intranet“. Some browsers behave differently when in Private mode, preventing silent authentication and forcing the user to manually enter or confirm an authentication request:

  • In Edge InPrivate, Edge Legacy’s InPrivate, and Firefox Private Mode, the browser will not automatically respond to a HTTP/401 challenge for Negotiate/NTLM credentials.
  • In IE InPrivate and Chrome/Brave Incognito (prior to v81), the browser will automatically respond to a HTTP/401 challenge for Negotiate/NTLM credentials.

Notes:

  • In Edge Legacy and the new Chromium-based Edge, the security manager returns MustPrompt when queried for URLACTION_CREDENTIALS_USE.
  • Unfortunately Edge Legacy’s Kiosk mode runs InPrivate, meaning you cannot easily use Kiosk mode to implement a display that projects a dashboard or other authenticated data on your Intranet.
  • For Firefox to support automatic authentication at all, the
    network.negotiate-auth.allow-non-fqdn and/or network.automatic-ntlm-auth.allow-non-fqdn preferences must be adjusted.

Accept-Language

In one of the clearest privacy/user-experience tradeoffs, Chromium now sends only the first Accept-Language rather than the full list when loading sites in Private Mode.

registerProtocolHandler

All common browsers block the HTML5 registerProtocolHandler API in private mode.

System Integrations

Browsers often will change their behavior for windows running in Private Mode when integrating with the operating system. These restrictions are intended to help prevent inadvertent disclosure or retention of Private Mode information outside of the Private Mode browser instance.

For example, some browsers will avoid handing thumbnails of Private Mode windows to the OS, which uses such thumbnails for task switching UI, history views, etc.

Similarly, Chromium intends to stop exposing media titles and thumbnails to the OS from Private Mode pages to avoid their display in the volume mixer, playback controls on the OS lock screen, etc.

On Android, the Edge Private Mode sets the flag to prevent screen captures (user-initiated or app-invoked) from capturing the image of the screen (which users may like, or may find annoying). While Windows exposes the SetWindowDisplayAffinity() API, that API is presently used only by DRM and not by Private Mode.

As of 2024, Chromium will flag data copied from an Incognito window to the clipboard as “Private” so that it does not go in Clipboard history or Windows’ Clipboard cloud sync.

When downloaded and SaveAs files are saved to disk, the Mark-of-the-Web has the original URLs removed and replaced with the generic about:internet:

Another concern surrounds invocation of other applications (e.g. via an AppProtocol) that could allow web content running in a private mode instance to trigger an app that would in turn make a request outside of InPrivate allowing user identification via, for example, that app’s automatic release of credentials. (Example)

Detection of Privacy Modes

While browsers generally do not try to advertise to websites that they are running inside Private modes, it can be relatively straightforward for a website to feature-detect this mode and behave differently. For instance, some websites like the Boston Globe block visitors in Private Mode (forcing login) because they want to avoid circumvention of their “Non-logged-in users may only view three free articles per month” paywall logic.

Sites can detect privacy modes by looking for the behavioral changes that signal that a given browser is running in Private mode; for instance, indexedDB is disabled in Edge Legacy while InPrivate. Detectors have been built for each browser and wrapped in JavaScript libraries. Defeating Private mode detectors requires significant investment on the part of browsers (e.g. “implement an ephemeral mode for indexedDB“) and fixes lagged until mainstream news sites (e.g. Boston Globe, New York Times) began using these detectors more broadly.

See also:

End users might successfully circumvent such checks using a manually-created “Ephemeral Profile”, a regular mode profile which is configured to delete all storage on exit.

Signaling Privacy Mode

Update: Perhaps surprisingly, the new Microsoft Edge sends a deliberate signal to (only) the user’s default search engine when it is loaded in InPrivate mode. The HTTPS request header PreferAnonymous: 1 is sent on requests to the server to allow it to avoid caching any data related to the user’s use of the search engine.

This header is sent only to the search engine, and not to other sites.

“Guest” Profile

Browsers based on Chromium often also have a Guest Browsing mode, which has a superset (mostly*) of Private Mode behavior.

When you “Browse as Guest”, the browser session treats itself as running in Private Mode, but unlike a normal Private Mode session, the session also starts with an empty user profile. That means the browser will not

  • show history entries in the address box or History page
  • list your bookmarks
  • offer to fill in your account information or passwords
  • offer to fill in autocomplete information
  • etc.

When you close all Guest Profile windows, your history and other state generated in the session is deleted, just like in regular Private mode.

Using a Guest Profile helps prevent you from accidentally leaking information to sites (e.g. inadvertently triggering an autofill).

* Internally to Chromium, most features check a boolean flag IsOffTheRecord() when deciding how to behave. This boolean returns true for both InPrivate/Incognito and Guest profiles. In rare cases, Guest is not designed to have InPrivate behavior. For instance, as of Edge 86, the PreferAnonymous header mentioned earlier is not sent for Guest profiles. Developers can distinguish InPrivate/Incognito from Guest like so:

Profile::FromBrowserContext(web_contents->GetBrowserContext())
                            ->IsIncognitoProfile());

Advanced Private Modes

Generally, mainstream browsers have taken a middle ground in their privacy features, trading off some performance and some convenience for improved privacy. Users who are very concerned about maintaining privacy from a wider variety of threat actors need to take additional steps, like running their browser in a discardable Virtual Machine behind an anonymizing VPN/Proxy service, disabling JavaScript entirely, etc.

The Brave Browser offers a “Private Window with Tor” feature that routes traffic over the Tor anonymizing network; for many users this might be a more practical choice than the highly privacy-preserving Tor Browser Bundle, which offers additional options like built-in NoScript support to help protect privacy.

Common Questions

Q: When I open a new InPrivate window, somehow I’m already signed in to some site I was previously using InPrivate. What gives?
A: Today, Chromium uses a single “browser session” for all Private Mode windows. See this post for discussion.

Q: From web content, can I create a link that automatically opens InPrivate? Something like <a href="http://secret.example.com" rel="private" />?
A: No. There’s presently no standard for this, and browsers cannot safely implement something like this until the browser supports multiple parallel web sessions for Private mode. Otherwise, website running outside of Private Mode could “push” an identifier into a running Private Mode session that would unmask the user.

Q: As an end-user, can I configure a given site to always open in Private mode?
A: Not yet. You can easily create a desktop shortcut that does so (just add the --inprivate command line flag), but that won’t impact navigations from the address bar or regular site-to-site links inside the browser. The Issue mentions another workaround. But it would be a cool feature to build in, right?

Related Links

-Eric

An Update on the Edge XSS Filter

In Windows 10 RS5 (aka the “October 2018 Update”), the venerable XSS Filter first introduced in 2008 with IE8 was removed from Microsoft Edge. The XSS Filter debuted in a time before Content Security Policy as a part of a basket of new mitigations designed to mitigate the growing exploitation of cross-site scripting attacks, joining older features like HTTPOnly cookies and the sandbox attribute for IFRAMEs.

The XSS Filter feature was a difficult one to land– only through the sheer brilliance and dogged persistence of its creator (the late David Ross) did the IE team accept the proposal that a client-side filtering approach could be effective with a reasonable false positive rate and good-enough performance to ship on-by-default. The filter was carefully tuned, firing only on cross-site navigation, and in need of frequent updates as security researchers inside and outside the company found tricks to bypass it. One of the most significant technical challenges for the filter concerned how it was layered into the page download pipeline, intercepting documents as they were received as raw text from the network. The filter relied evaluating dynamically-generated regular expressions to look for potentially executable markup in the response body that could have been reflected from the request URL or POST body. Evaluating the regular expressions could prove to be extremely expensive in degenerate cases (multiple seconds of CPU time in the worst cases) and required ongoing tweaks to keep the performance costs in check.

In 2010, the Chrome team shipped their similar XSS Auditor feature, which had the luxury of injecting its detection logic after the HTML parser runsdetecting and blocking reflections as they entered the script engine. By throttling closer to the point of vulnerability, its performance and accuracy is significantly improved over the XSS Filter.

Unfortunately, no matter how you implement it, clientside XSS filtration is inherently limited– of the four classes of XSS Attack, only one is potentially mitigated by clientside XSS filtration. Attackers have the luxury of tuning their attacks to bypass filters before they deploy them to the world, and the relatively slow ship cycles of browsers (6 weeks for Chrome, and at least a few months for IE of the era) meant that bypasses remained exploitable for a long time.

False positives are an ever-present concern– this meant that the filters have to be somewhat conservative, leading to false-negative bypasses (e.g. multi-stage exploits that performed a same-site navigation) and pronouncements that certain attack patterns were simply out-of-scope (e.g. attacks encoded in anything but the most popular encoding formats).

Early attempts to mitigate the impact of false positives (by default, neutering exploits rather than blocking navigation entirely) proved bypassable and later were abused to introduce XSS exploits in sites that would otherwise be free of exploit (!!!). As a consequence, browsers were forced to offer options that would allow a site to block navigation upon detection of a reflection, or disable the XSS filter entirely.

Surprisingly, even in the ideal case, best-of-class XSS filters can introduce information disclosure exploits into sites that are free of XSS vulnerabilities. XSS filters work by matching attacker-controlled request data to text in a victim response page, which may be cross-origin. Clientside filters cannot really determine whether a given string from the request was truly reflected into the response, or whether the string is naturally present in the response. This shortcoming creates the possibility that such a filter may be abused by an attacker to determine the content of a cross-origin page, a violation of Same Origin Policy. In a canonical attack, the attacker frames a victim page with a string of interest in it, then attempts to determine that string by making a series of successive guesses until it detects blocking by the XSS filter. For instance, xoSubframe.contentWindow.length exposes the count of subframes of a frame, even cross-origin. If the XSS filter blocks the loading of a frame, its subframe count is zero and the attacker can conclude that their guess was correct.

In Windows 10 RS4 (April 2018 update), Edge shipped its implementation of the Fetch standard, which redefines how the browser downloads content for page loads. As a part of this massive architectural shift, a regression was introduced in Edge’s XSS Filter that caused it to incorrectly determine whether a navigation was cross-origin. As a result, the XSS Filter began running its logic on same-origin navigations and skipping processing of cross-origin navigations, leading to a predictable flood of bug reports.

In the process of triaging these reports and working to address the regression, we concluded that the XSS Filter had long been on the wrong side of the cost/benefit equation and we elected to remove the XSS Filter from Edge entirely, matching Firefox (which never shipped a filter to begin with).

We encourage sites that are concerned about XSS attacks to use the client-side platform features available to them (Content-Security-Policy, HTTPOnly cookies, sandboxing) and the server-side patterns and frameworks that are designed to mitigate script injection attacks.

-Eric Lawrence