Updating Browsers Quickly: Flags, Respins, and Components

By this point, most browser enthusiasts know that Chrome has a rapid release cycle, releasing a new stable version of the browser approximately every six 4 weeks (2022 Update: now every four weeks). The Edge team adopted that rapid release cadence for our new browser, and we’re already releasing new Edge Dev Channel builds every week.

What might be less obvious is that this six four week cadence represents an upper-bound for how long it might take for an important change to make its way to the user.

Background: Staged Rollouts

Chrome uses a staged rollout plan, which means only a small percentage (1%-5%) of users get the new version immediately. If any high-priority problems are flagged by those initial users, the rollout can be paused while the team considers how to best fix the problem. That fix might involve shipping a new build, turning off a feature using the experimentation server, or dynamically updating a component.

Let’s look at each.

Respins

If a serious security or functionality problem is found in the Stable Channel, the development team generates a respin of the release, which is a new build of the browser with the specific issue patched. The major and minor version numbers of the browser stay the same. For instance, on July 15th, Chrome Stable version 75.0.3770.100 was updated to 75.0.3770.142. Users who had already installed the buggy version in the channel are updated automatically, and users who haven’t yet updated to the buggy version will just get the fixed version when the rollout reaches them.

If you’re curious, you can see exactly which versions of Chrome are being delivered from Google’s update servers for each Channel using ChromiumDash.

Field Trial Flags

In some cases, a problem is discovered in a new feature that the team is experimenting with. In these cases, it’s usually easy for the team to simply remotely disable or reconfigure the experiment as needed using the experimental flags. The browser client periodically polls the development team’s servers to get the latest experimental configuration settings. Chrome codenames their experimental system “Finch,” while Microsoft calls ours “ECS” (Experimental Control System”) or “CFR” (Controlled Feature Rollout).

You can see your browser’s current field trial configuration by navigating to

chrome://version/?show-variations-cmd

The hexadecimal Variations list is generally inscrutable, but the Command-line variations section later in the page is often more useful and allows you to better understand what trials are underway. You can even use this list to identify the exact trial causing a particular problem.

Regular readers might remember that I’ve previously written about Chrome’s Field Trials system. You can find some documentation of Edge’s ECS here: https://docs.microsoft.com/en-us/deployedge/edge-configuration-and-experiments. The tl;dr is that you must ensure https://config.edge.skype.com is reachable and you should think long and hard before setting the ExperimentationAndConfigurationServiceControl policy.

Update: Chrome does not publish their field-trial configuration in human-readable form, but here is a tool to grab the complete configuration data from Google directly. In contrast, Edge’s configuration data is sent on a per-client basis, so there’s no way for the public to see the configuration for the entire population.

Components

In other cases, a problem is found in a part of the browser implemented as a “Component.” Components are much like hidden, built-in extensions that can be silently and automatically updated by the Component Updater.

The primary benefit of components is that they can be updated without an update to Chrome itself, which allows them to have faster (or desynchronized) release cadences, lower bandwidth consumption, and avoids bloat in the (already sizable) Chrome installer. The primary drawback is that they require Chrome to tolerate their absence in a sane way.

To me, the coolest part of components is that not only can they update without downloading a new version of the browser, in some cases users don’t even need to restart their browser to begin using the updated version of a component. As soon as a new version is downloaded, it can “take over” from the prior version.

To see the list of components in the browser, visit

chrome://components

In my Chrome Canary instance, I see the following components:

Components

As you can see, many of these have rather obtuse names, but here’s a quick explanation where I know offhand:

  • MEI Preload – Policies for autoplay (see chrome://media-engagement/ )
  • Intervention Policy – Controls interventions used on misbehaving web pages
  • Third Party Module – Used to exempt accessibility and other components from the Code Integrity protections on the browser’s process that otherwise forbid injection of DLLs.
  • Subresource Filter Rules – The EasyList adblock database used by Chrome’s built-in adblocker to remove ads from a webpage when the Safe Browsing service indicates that a site violates the guidelines in the Better Ads Standard.
  • Certificate Error Assistant – Helps users understand and recover from certificate errors (e.g. when behind a known WiFi captive portal).
  • Software Reporter Tool – Collects data about system configuration / malware.
  • CRLSet – List of known-bad certificates (used to replace OCSP/CRL).
  • pnacl – Portable Native Client (overdue for removal)
  • Chrome Improved Recovery Unsure, but comments suggest this is related to helping fix broken Google Updater services, etc.
  • File Type Policies – Maps a list of file types to a set of policies concerning how they should be downloaded, what warnings should be presented, etc. See below.
  • Origin Trials – Used to allow websites to opt-in to experimenting with future web features on their sites. Explainer.
  • Adobe Flash Player – The world’s most popular plugin, gradually being phased out; slated for complete removal in late 2020.
  • Widevine Content DecryptionA DRM system that permits playback of protected video content.

If you’re using an older Chrome build, you might see:

If you’re using Edge, you might see:

  • Trust Protection Lists – The list of known trackers and organizational lists used by the Tracking Prevention feature. Presently, this list derives from Mozilla’s Content Blocking list, which in turn derives from Disconnect.me‘s list.
  • Zxcvbn Data Dictionaries – Password strength evaluator metadata(?)
  • Edge AutoLaunch Protocols – Allow certain sites to launch certain protocols without a prompt. See this post.
  • Domain Actions – Allow Edge to apply certain compatibility overrides on specific sites.
  • Well Known Domains – A list of popular and generally reputable sites used by a variety of features.

If you’re using the Chromium-derived Brave browser, you’ll see that brave://components includes a bunch of extra components, including “Ad Blocker”, “Tor Client”, “PDF Viewer”, “HTTPS Everywhere”, and “Local Data Updater.”

If you’re using Chrome on Android, you might notice that it’s only using three components instead of thirteen; the missing components simply aren’t used (for various reasons) on the mobile platform. As noted in the developer documentation, “The primary drawback [to building a feature using a Component] is that [Components] require Chrome to tolerate their absence in a sane way.

Edge checks for updated versions of components one minute after startup, then every five hours after that.

Case Study: Fast Protection via Component Update

Let’s take a closer look at my favorite component, the File Type Policies component.

When the browser downloads a file, it must make a number of decisions for security reasons. In particular, it needs to know whether the file type is potentially harmful to the user’s device. If the filetype is innocuous (e.g. plaintext), then the file may be downloaded without any prompts. If the type is potentially dangerous, the user should be warned before the download completes, and security features like SafeBrowsing/SmartScreen should scan the completed download for malicious content.

In the past, this sort of “What File Types are Dangerous?” list was hardcoded into various products. If a file type were later found to be dangerous, patching these products with updated threat information required weeks to months.

In contrast, Chrome delivers this file type policy information using the File Type Policies component. The component lets Chrome engineers specify which types are dangerous, which types may be configured to automatically open, which types are archives that contain other files that may require scanning, and so on.

How does this work in the real world? Here’s an example.

Around a year ago, it was discovered that files with the .SettingContent-ms file extension could be used to compromise the security of the user’s device. Browsers previously didn’t take any special care when such files were downloaded, and most users had no idea what the files were or what would happen if they were opened. Everyone was caught flat-footed.

Less than a day after this threat came to light, a Chrome engineer simply updated a single file to mark the settingcontent-ms file type as potentially dangerous. The change was picked up by the component builder, and Chrome users across all versions and channels were protected as their browser automatically pulled down the updated component in the background within hours.

Ever faster!

-Eric

Appendix A: Component Update Policy

This information is current as of October 2020 but will likely change over time.

The Microsoft Edge ComponentUpdatesEnabled policy can be set to default (unset), enabled, or disabled.  In the case that a component supports the policy and the policy is disabled, the update will be blocked.  Otherwise, the component will be updated when a new version is found.

Component NameCan be disabled by policyApproximate size of CRX
Adobe FlashYes13MB
Subresource Filter RulesYes77kB
Trust Protection ListsYes54kB
Edge Improved RecoveryYesNot in use
CRLSetNo22kB
Origin TrialsNoLess than 1kB
Widevine Content Decryption ModuleYes5MB

Currently, only CRLSet and Origin Trials forcibly download their updates.

The update check step is a request asking what updates exist, and is very small (>1kB typically).  The CRLSet and Origin Trials will download based upon that information either way, but they are also fairly small.

The update check also allows the browser to know if components like Flash or Widevine are too old to use safely.  Widevine ships with a version bundled into the browser, and updates every few months otherwise; leaving updates disabled for it could impact viewing of some DRM protected videos until the next browser release.

Flash updates monthly, but is only sent to users that already have it or who request it (in edge://settings/content/flash).  The Flash configuration can additionally be disabled by Flash/Plugin specific group policies; see DefaultPluginsSetting – and set that to Block.  Then Flash will not update, even if other component updates are enabled. 2024 Update: Flash was removed years ago.

The update check runs every five hours; if configured for fast update, it starts 10 seconds after browser launch, but more normally 5 minutes after browser launch.  (Extensions do update after 10 seconds though, and they likely look similar from a network perspective.)

The Edge Browser installer includes the then-current version of only some of the components, so disabling ComponentUpdates should not break your browser, but users may have a suboptimal experience (because compatibility problems or other issues may be fixed by unavailable or outdated components).

Challenges with Federated Identity in modern browsers

Many websites offer a “Log in” capability where they don’t manage the user’s account; instead, they offer visitors the ability to “Login with <identity provider>.”

When the user clicks the Login button on the original relying party (RP) website, they are navigated to a login page at the identity provider (IP) (e.g. login.microsoft.com) and then redirected back to the RP. That original site then gets some amount of the user’s identity info (e.g. their Name & a unique identifier) but it never sees the user’s password.

Such Federated Identity schemes have benefits for both the user and the RP site– the user doesn’t need to set up yet another password and the site doesn’t have to worry about the complexity of safely storing the user’s password, managing forgotten passwords, etc.

In some cases, the federated identity login process (typically implemented as a JavaScript library) relies on navigating the user to a top-level page to log in, then back to the relying party website into which the library injects an IFRAME1 back to the identity provider’s website.

FederatedID

The authentication library in the RP top-level page communicates with the IP subframe (using postMessage or the like) to get the logged-in user’s identity information, API tokens, etc. [Here’s a demo].

In theory, everything works great. The IP subframe in the RP page knows who the user is (by looking at its own cookies or HTML5 localStorage or indexedDB data) and can release to the RP caller whatever identity information is appropriate.

Crucially, however, notice that this login flow is entirely dependent upon the assumption that the IP subframe is accessing the same set of cookies, HTML5 storage, and/or indexedDB data as the top-level IP page. If the IP subframe doesn’t have access to the same storage, then it won’t recognize the user as logged in.

Unfortunately, this assumption has been problematic for many years, and it’s becoming even more dangerous over time as browsers ramp up their security and privacy features.

The root of the problem is that the IP subframe is considered a third-party resource, because it comes from a different domain (identity.example) than the page (news.example) into which it is embedded.

For privacy and security reasons, browsers might treat third-party resources differently than first-party resources. Examples include:

  1. The Block 3rd Party cookies option in most browsers
  2. The SameSite Cookie attribute
  3. P3P cookie blocking in Internet Explorer2
  4. Zone Partitioning in Internet Explorer and Edge Spartan3
  5. Safari’s Intelligent Tracking Protection
  6. Firefox Content Blocking
  7. Firefox State Partitioning
  8. Microsoft Edge Tracking Prevention

When a browser restricts access to storage for a 3rd party context, our theoretically simple login process falls apart. The IP subframe on the relying party doesn’t see the user’s login information because it is loaded in a 3rd party context. The authentication library is likely to conclude that the user is not logged in, and redirect them back to the login page. A frustrating and baffling infinite loop may result as the user is bounced between the RP and IP.

The worst part of all of this is that a site’s login process might usually work, but fail depending on the user’s browser choice, browser configuration, browser patch level, security zone assignments, or security/privacy extensions. As a result, a site owner might not even notice that some fraction of their users are unable to log in.

So, what’s a web developer to do?

The first task is awareness: Understand how your federated login library works — is it using cookies? Does it use subframes? Is the IP site likely to be considered a “Tracker” by popular privacy lists?

The second task is to build designs that are more resilient to 3rd-party storage restrictions:

  • Be sure to convey the expected state from the Identity Provider’s login page back to the Relying Party. E.g. if your site automatically redirects from news.example to identity.example/login back to news.example/?loggedin=1, the RP page should take note of that URL parameter. If the authentication library still reports “Not signed in”, avoid an infinite loop and do not redirect back to the Identity Provider automatically.
  • Authentication libraries should consider conveying identity information back to the RP directly, which will then save that information in a first party context. For instance, the IP could send the identity data to the RP via a HTTP POST, and the RP could then store that data using its own first party cookies.
  • For browsers that support it, the Storage Access API may be used to allow access to storage that would otherwise be unavailable in a 3rd-party context. Note that this API might require action on the part of the user (e.g. a frame click and a permission prompt).

The final task is verification: Ensure that you’re testing your site in modern browsers, with and without the privacy settings ratcheted up.

-Eric

[1] The call back to the IP might not use an IFRAME; it could also use a SCRIPT tag to retrieve JSONP, or issue a fetch/XHR call, etc. The basic principles are the same.
[2] P3P was removed from IE11 on Windows 10.
[3] In Windows 10 RS2, Edge 15 “Spartan” started sharing cookies across Security Zones, but HTML5 Storage and indexedDB remain partitioned.

Surprise: Undead Session Cookies

I’ve been working on browsers professionally for 12 of the last 15 years, and in related areas for 20 of the last 20, and over the years I’ve discovered enough surprises in browser behavior that they’re no longer very surprising.

Back in April, I wrote up a quick post explaining how easy it is to delete a single site’s cookies in the new Edge browser. That post was written in response to a compatibility problem with some internal web application that could somehow get in a state where a single “bad” cookie would cause the application to fail to load. The team that owns the application later looked into things further and discovered that the problem was that the application was misbehaving upon receipt of a very old (over a month) session cookie.

Recall that there are two types of cookies:

  • Persistent cookies, sent to the server until the expiration date supplied when they were set, or until the user clears their cookies, whichever happens first, and
  • Session cookies, sent to the server until the end of the user’s browser session.

Now, in most cases, developers expect that Persistent cookies will live longer than Session cookies– most users restart their browsers (or computers) every few days, and many modern browsers require restart (to install updates) every few weeks. In contrast, many Persistent cookies are configured to last for a year or more.

So how did this zombie cookie live so long?

Until last week, I didn’t realize that these browser settings in Chrome/Edge76:

OnStartup

…and Firefox:

FirefoxOnStartup

…both behave very differently than the old setting from Internet Explorer:

IEOnStartup

…and the old setting from Edge 18 (Spartan) and earlier:

EdgeOnStartup

The Internet Explorer and Edge 18 settings simply open tabs to the URLs of the tabs that were open when you last closed your browser.

In contrast, the Firefox/Chrome/Edge76+ settings restore the browser session itself… which means that closing the browser does not delete your session cookies and doesn’t empty the HTML5 sessionStorage (Update: Chromium broke sessionStorage recovery in Chrome 77, twenty days after this post).

In many ways, preserving session state makes sense– without it, users are likely to find that their restored tabs are immediately navigated to a login page when the browser is restarted. However, a consequence of this session restoration behavior is that browsers with this option configured might keep session cookies alive for a very long time:kpk.png

If you’d like to play with your browser’s behavior, try setting the option and then play with this simple test page. (The background of the page is generated by the session cookie, and the sessionStorage and localStorage values are shown in the text of the page. Adjust the dropdown to change the color.)

Note: If the Chromium-based browser is restarted by visiting chrome://restart or if it restarts to install an update, it behaves as if “Continue where I left off” is set, even if it isn’t.

Web Developers: Given this session resumption behavior, it’s more important than ever to ensure that your site behaves reasonably upon receipt of an outdated session cookie (e.g. redirect the user to the login page instead of showing an error).

Users: If you enable the session resumption option, keep in mind that you can’t simply close your browser to “log out” of a site– you need to explicitly use the site’s logout option (I’ve written about this before).

-Eric

PS: If you’re really concerned about privacy, you can set the Keep local data only until you quit your browser option:

KeepLocal

This will clear all Session and Persistent storage areas every time you exit your browser, regardless of whether you’ve set the “On Startup: Continue where you left off”.

PS2: What else is bound to “session lifetime”? Client Hint opt-in, for one thing.

Edge79+ vs. Edge18 (Edge Legacy) vs. Chrome vs. Internet Explorer

Note: I expect to update this post over time. Last update: Sept 29, 2025.

Compatibility Deltas

As our new Edge Insider builds roll out to the public, we’re starting to triage reports of compatibility issues where Edge79+ (the new Chromium-based Edge, aka Anaheim) behaves differently than the old Edge (Edge18, aka Spartan, aka Edge Legacy) and/or Google Chrome.

In general, Edge79+ will behave very similarly to Chrome. When comparing Edge and Chrome behavior, be sure to compare against the corresponding Chrome Stable, Beta, Dev and Canary channels; Edge 80 vs Chrome 80, etc.

We expect there will be some behavioral deltas between Edge79+ and its Chrome-peer versions, so I’ll note those here too.

Note: I’ve previously blogged about interop issues between Edge18 and Chrome.

  • For security reasons, Edge79 and Chrome block navigation to file:// URLs from non-file URLs, although Edge 95+ has a policy to somewhat relax that.
  • In Edge18 and Internet Explorer, attempting to navigate to an App Protocol with no handler installed shows a prompt to visit the Microsoft Store to find a handler. In Chrome/Edge79, the navigation attempt is silently ignored.
  • Edge 18 and Internet Explorer offer a msLaunchUri API for launching and detecting App Protocols. This API is not available in Edge 79 or Chrome.
  • Edge 18 and Internet Explorer allow an App Protocol handler to opt-out of warning the user on open using the WarnOnOpen registry key. Edge 79 and Chrome do not support this registry key.

Rendering

  • Edge shows a Password Reveal icon on password fields; Chrome does not do so on Desktop. If your website shows its own reveal icon, you should use CSS to hide Edge’s.
  • When rendering text/plain files, Chromium wraps the lines of text to the width of the browser window, while Internet Explorer did not. If you need to turn off word-wrapping, you can add a Favorite named NoWrap with the url value:

    javascript:document.body.firstChild.style =
    "word-wrap:none";void(0);

    Clicking the Favorite will disable word-wrapping on the loaded text file.

Formats

  • Edge/Chrome have better support for image formats vs. Internet Explorer; e.g. Chromium supports WebP and Animated PNGs. Currently, Edge does not support AVIF while Chrome does.
  • Edge/Chrome have comparatively poor support for the MHTML format.

Downloads

  • Unlike IE/Edge18, Edge79/Chrome do not support DirectInvoke, a scheme whereby a download is converted into the launch of an application with a URL argument. DirectInvoke is most commonly used when launching Office documents and when running ClickOnce applications. For now, users can workaround the lack of ClickOnce support by installing an extension. Update: In Edge 87+, DirectInvoke is enabled; to enable ClickOnce, see the edge://flags/#edge-click-once setting.
  • Edge79/Chrome do not support the proprietary msSaveBlob or msSaveOrOpenBlob APIs supported in Edge18. In most cases, you should instead use an A element with a download attribute.
  • Edge18 did not support navigation to or downloading from data URLs via the download attribute; Edge79/Chrome allow the download of data URLs up to 2mb in length. In most cases, you should prefer blob urls.

Network Proxy

  • Edge 79+/Chrome adopt the system’s proxy settings by default. If a proxy script is supplied, it is evaluated using the built-in V8 script engine. In contrast, Edge 18 and earlier use the WinHTTP Proxy Service in Windows. Further discussion of the implications of this difference can be found at the end of this post.

    Microsoft DirectAccess and similar networking software may not work properly when Chromium performs proxy determination internally. You can instruct Edge 79+ to use the WinHTTP Proxy Service by launching the browser with the –winhttp-proxy-resolver command line argument. This feature can be configured via Group Policy.
  • Edge 79+/Chrome do not allow loading a PAC script from a file:// URI. (IE allowed this long ago, but it hasn’t been supported in a long time).
  • If you are behind an authenticating proxy server and choose to save your proxy username/password in Edge 18 or Internet Explorer, the WinHTTP Proxy Service will store those credentials in CredMan and reuse those proxy credentials for subsequent challenges even if you restart the browser. The credentials will also be available for silent use by other applications (e.g. the Windows Store app). Even if you don’t tick the “Remember my credentials” checkbox, the proxy credentials will be remembered for the Windows logon session.

    In contrast, if you choose to save your proxy username/password in Edge79+, Chrome, or Firefox, you will be shown an authentication prompt once every time you restart your browser. The username:password will be pre-filled but you must hit “OK” to submit the credentials. Stored credentials will not be available for use by other applications that wish to use the proxy.
  • The myIpAddress() function allows a proxy to determine (one) IP address for the client. However, this function is inherently broken for multi-homed hosts (which have multiple IPs). To decide which IP address to return, Chromium asks the system for a route to an IP address on the Internet.

    Chrome probes the route to 8.8.8.8 / 2001:4860:4860::8888 while Edge 79+ probes the route to 131.107.255.255 / fd3e:4f5a:5b81::1. The probe doesn’t actually send any packets to the target, but if your network is configured to route these addresses differently, your PAC file’s myIpAddress() call could return different results between Chrome and Edge.

Network Protocols & Cache

  • Chrome/Edge79+ support the HTTP3/QUIC protocol. Edge 18 and earlier do not.
  • Generally, Chrome/Edge79+ have enhanced support for caching directives, fixing many limitations in the IE/Edge18 cache. However, one delta is that Chrome/Edge79+ do not respect Cache-Control directives specified by META HTTP-EQUIV directives inside a HTML page. Caching directives must instead be specified in the appropriate HTTP Response headers.
  • Chrome/Edge have removed support for the FTP protocol.
  • Edge Legacy supports the Cache-Control token immutable; Chromium-based browsers do not (yet).
  • Chrome/Edge/Safari will fail responses served over HTTP2 with ERR_HTTP2_PROTOCOL_ERROR if a response header name contains a space. Edge Legacy/Firefox will just ignore the header, and IE will allow it.
  • Chrome/Edge limit granularity of the HTTP Referer header (and the DOM document.referrer property) by default.
  • Internet Explorer historically had a URL Length limit of 2083 (INTERNET_MAX_URL_LENGTH) characters. Over the years, this was relaxed in many places, but not everywhere. Chromium limits URLs to 2mb (kMaxURLChars) for most scenarios, although the URL bar limits the displayed URL to 32kb, and some components that interact with Windows (e.g. marking of downloaded files) will limit URLs passed to those components to INTERNET_MAX_URL_LENGTH for interoperability.
  • Chrome will attempt to limit itself to 20 redirects (kMaxRedirects = 20) when fetching from the network before returning ERR_TOO_MANY_REDIRECTS (Demo). Interestingly, as of Chrome 124, the network error page itself contains a NetErrorAutoReloader that will keep chasing redirects in the background based on its own logic, meaning that navigations that require more than 20 redirects may succeed.

Network Timeouts

Receive timeout

Internet Explorer had a default receive timeout of one hour (a read() call on a socket will timeout after one hour), that could be controlled via the registry. In contrast, I don’t see an attempt to set a timeout in the Chromium code, or a requirement in fetch. Experimenting locally, a top-level navigation does not time out in over an hour. Chromium uses non-blocking reads, so SO_RCVTIMEO is not set, and I don’t see any obvious timeouts in higher levels.

Other timeout trivia

HTTPS – TLS Protocol

  • Edge 113+ and Chrome (around 112+) changed to use the Chromium certificate verifier by default. This verifier has slightly different behavior than the old Windows verifier; see this post for details.
  • Edge79 and Chrome enable TLS/1.3 by default; Edge18 does not support TLS/1.3 prior to Windows 10 19H1, and even on that platform it is disabled by default (and known to be buggy).
  • Edge79 and Chrome support a different list of TLS ciphers than Edge18.
  • Edge79 and Chrome send GREASE tokens in HTTPS handshakes; Edge18 does not.
  • Edge79, Chrome, Firefox, and Safari prohibit connections for HTTP/2 traffic from using banned (weak) ciphers, showing ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY if the server attempts to use such ciphers. Edge18 did not enforce this requirement. This has primarily impacted intranet websites served by IIS on Windows Server 2012 where the server was either misconfigured or does not have the latest updates installed. Patching the server and/or adjusting its TLS configuration will resolve the problem. End-users should complain to the server operators, and can work around the problem by closing all instances of Edge then restarting with a commandline argument msedge.exe –disable-http2 to disable support for the faster network protocol.
  • Chrome and Edge use the HSTS Preload list checked into Chromium and generated by HSTSPreload.org. Updates to that list HSTS updates flow through the release channels, so if you get your site added or removed from the HSTS Preload list this week, it’ll be in the Stable release in ~8-12 weeks (0-4 weeks of Canary, 4 weeks of Dev, 4 weeks of beta).

    Perhaps surprisingly, Edge/Chrome do not update the HSTS preload list out-of-band (via the component updater). Internet Explorer (WinINET) updates their version of the list (which is based on Mozilla’s list, which is based on Chromium’s list) very irregularly (~twice a year).

HTTPS – Certificates

  • Edge79 and Chrome require that a site’s certificate contain its domain name in the SubjectAltName (SAN) field. Edge 18 permits the certificate to omit the SAN and if the domain name is in the Subject Common Name (CN) field. (All public CAs use the SAN; certificates that chain to a local/enterprise trusted root may need to be updated).
  • Firefox, Edge79 and Chromium 41+, reject wildcarded SubjectAltNames where the * is not the entirety of the leftmost label. In contrast, Edge18 and IE will match a SubjectAltName of w*.example.com to the server www.example.com while the modern browsers will not.
  • Edge79 and Chrome do not allow server certificate chains that contain SHA-1 signatures. Edge Legacy and IE permit SHA-1 in chains that certificates that chain to a local/enterprise root. A policy added Edge 86 temporarily allowed SHA-1 chains but this policy is no longer available.
  • Edge79 and Chrome require certificates that chain to trusted root CAs to be logged in Certificate Transparency (CT). This generally isn’t a problem because public roots are supposed to log in CT as a part of their baseline requirements. However, certain organizations (including Microsoft and CAs) have hybrid roots which are both publicly trusted and issue privately within the organization. As a result, loading pages may error out with NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED. To mitigate this, such organizations must either start logging internal certificates in CT, or set one of three policies under HKLM\SOFTWARE\Policies\Microsoft\Edge. Edge18 does not support CT.
  • For most (non-EV) certificates, Chrome/Edge79 will not request certificate revocation information from the network (OCSP, CRL download), using revocation information only if it’s cached on the client or stapled in the TLS handshake. Internet Explorer and Legacy Edge would actively hit the network for revocation information by default. See What’s the story with certificate revocation? for discussion.
  • Edge79 and Chrome use a custom Win32 client certificate picker UI (Edge’s is slightly enhanced vs. Chrome’s), while Edge18 uses the system’s default certificate picker.

Cookies

Authentication and Login

  • In Edge79, Edge18, and Firefox, running the browser in InPrivate mode disables automatic Integrated Windows Authentication. Chrome (prior to v81) and Internet Explorer do not disable automatic authentication in private mode. You can disable automatic authentication in Chrome by launching it with a command line argument: chrome.exe --auth-server-whitelist="_"

    Starting in Edge 82, flags on the edge://flags page allow re-enabling Automatic Authentication for Guest and InPrivate sessions.
  • Edge18/Edge79 integrates a built-in single-sign-on (SSO) provider, such that configured account credentials are automatically injected into request headers for configured domains; this feature is disabled in InPrivate mode. Chrome does not have this behavior for Microsoft accounts.
  • Edge18 supports Azure Active Directory’s Conditional Access feature. For Chrome, an extension is required. Edge79 has not yet integrated support for this feature.
  • Chrome and Edge 79+ choose the strongest HTTP Authentication scheme advertised by the server, regardless of the order of WWW-Authenticate headers provided. In contrast, Edge 18/IE prioritize the first non-BASIC scheme offered. This can lead the new Edge to choose Negotiate in cases where the older Edge might pick NLTM.
  • By default, Internet Explorer and Edge Legacy would automatically send a client certificate to a server on your Local Intranet if the client only had one certificate available. In Chromium, a Group Policy must be set.
  • Edge Legacy and IE encode BASIC authentication credentials using iso-8859-1. Chromium, Safari, and Firefox use UTF-8 as suggested by RFC7617.

WebAPIs

  • Edge18 includes an API window.external.GetHostEnvironmentValue that returns some interesting information about the system, including whether it is running in the “Windows 10 S” lockdown mode. Edge76 and Chrome do not support this API. Update: Edge 78 restored this API with a limited set of tokens:
    {“os-architecture”:”AMD64″,”os-build”:”10.0.18362″,”os-sku”:”4″,”os-mode”:”2″}. The os-mode of 2 indicates a Windows 10 S configuration.
  • Edge18 and Internet Explorer are the only browsers that consider all local-PC file:// URIs to be same-origin, allowing them to refer to other resources on the local computer. Other browsers treat file origins as unique, blocking DOM interactions between frames from different files, blocking transform of XML files using adjacent XSL files, etc.
  • Google Chrome ships with the Portable Native Client plugin; Edge79 does not include this plugin. The plugin is little-used and you’re unlikely to encounter problems with its absence except on the Google Earth website. PNaCl is deprecated in favor of WebAssembly and is slated to be removed from Chrome in Q2 2019.
  • The Edge Platform Status site also includes a short list of features that are supported in Edge18 but not Chromium-derived browsers.
  • The HTML5 SpeechRecognition API fails silently in Edge 76 to 82 because we do not connect to Google Web Services and have not yet done the work to hook this API up to a Microsoft Web Service. Forum thread. Update: This works on Windows as of Edge 100, but appears to be unreliable on Mac. The team is investigating.
  • The Edge Legacy and IE implementation offer only a limited set of WebCrypto algorithms (Test page) and have some undocumented limits (e.g. you must use a 12byte IV for AES-GCM). The Chromium implementation offers better support for a wider variety of scenarios.

Web APIs – Popups

The HTML5 standard for handling of the third argument (windowfeatures) to window.open() has changed between the time of Internet Explorer’s MSHTML and Chromium’s Blink. You can see the changes tracked on ChromeStatus. Notably, the use of windowfeatures today is largely only to determine whether the new window will be a tab or a popup, and does not control the presence of individual UI elements or resize behavior as it did in the IE3 era.

In Chromium today, the resizable windowFeature (passed in calls to window.open()) is used as a signal about whether the new window should contain the toolbar and be launched as a popup rather than a new tab; it does not prevent resizing of that popup. Notably, neither Firefox nor Safari allows this windowFeature to prevent the user from resizing the resulting browser window; only IE respects the directive.

Expect IEMode to behave the same way that IE did, and Edge mode to behave the way that Chromium Blink and HTML5 standard calls for. Chromium complies with the current version of the spec, which determines whether to create a popup window vs a new tab thusly:

To check if a popup window is requested, given tokenizedFeatures:

If tokenizedFeatures is empty, then return false.

If tokenizedFeatures["popup"] exists, then return the result of parsing tokenizedFeatures["popup"] as a boolean feature.

Let location be the result of checking if a window feature is set, given tokenizedFeatures, "location", and false.

Let toolbar be the result of checking if a window feature is set, given tokenizedFeatures, "toolbar", and false.

If location and toolbar are both false, then return true.

Let menubar be the result of checking if a window feature is set, given tokenizedFeatures, menubar", and false.

If menubar is false, then return true.

Let resizable be the result of checking if a window feature is set, given tokenizedFeatures, "resizable", and true.

If resizable is false, then return true.

Let scrollbars be the result of checking if a window feature is set, given tokenizedFeatures, "scrollbars", and false.

If scrollbars is false, then return true.

Let status be the result of checking if a window feature is set, given tokenizedFeatures, "status", and false.

If status is false, then return true.

Return false.

Group Policy and Command Line Arguments

Edge79+’s Policy List includes a set of Group Policies that match Chrome’s Group Policies and Edge’s command line arguments generally match Chrome’s. The minor changes include:

If you’re using the registry to set a policy for Edge, put it under the

HKEY_CURRENT_USER\Software\Policies\Microsoft\Edge

…node instead of under the

HKEY_CURRENT_USER\Software\Policies\Google\Chrome

node.

If you’re trying to use a Chrome command line argument when launching in the new MSEdge.exe and it’s not working, check whether it has “blacklist” or “whitelist” in the name. If so, we probably renamed it.

For instance, want to tell Edge not to accept a 3DES ciphersuite for TLS? You need to use

msedge.exe --cipher-suite-denylist=0x000a

…instead of

chrome.exe --cipher-suite-blacklist=0x000a

….as you would with Chrome.

Zones

Chrome and Edge 79+ make very limited use of the Windows Security Zones architecture. See this post for more information.

In contrast, Internet Explorer made extensive use of Zones to control security and compatibility changes. By way of example, back in IE8, a change was made to the browser such that the file upload control would no longer send the full path of an uploaded file. For instance, when uploading C:\users\ericlaw\desktop\ThisIsMyFile.jpg to the server, the browser would instead send C:\fakepath\ThisIsMyFile.jpg. The main reason is privacy: we didn’t want the remote website to be able to infer that the user uploading the file was named EricLaw based on the information about their local file system paths. This redaction became a web standard—now ALL browsers send fakepath and doing so is required by the HTML specification. However, for compatibility reasons, Internet Explorer would only apply this protection to sites in the Internet Zone; sites loaded in the Intranet zone would continue to send the unredacted file path. This behavior is controlled by the “Include local directory path when uploading files to a server” setting in the Internet Control Panel:

Modern browsers, including Microsoft Edge, do not have any setting like this one; there’s no way to turn off the redaction of the file path. Available workarounds would be to either 1) Put this site in IE Mode, or to 2) restructure the application such that it does not attempt to get a local file path from the user using a File Upload control.

Extensions

Generally speaking, nearly all Chrome extensions should work inside Edge. Exceptions include:

  1. When the “Extension” is really a Chrome App (these are deprecated by Google, and were never supported by Edge)
  2. The Extension uses Native Client (Edge does not support Native Client, and it’s been deprecated by Google)
  3. The Extension uses one of the Chrome-only APIs:
    chrome.gcm, chrome.identity.getAccounts, chrome.instanceID, chrome.identity.getAuthToken

User-Agent

Browsers identify themselves to servers using a User-Agent header. A top source of compatibility problems is caused by sites that attempt to behave differently based on the User-Agent header and make incorrect assumptions about feature support, or fail to update their checks over time. Please, for the love of the web, avoid User-Agent Detection at all costs!

Chrome User-Agent string:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36

Edge77 Beta (Desktop) User-Agent string:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.19 Safari/537.36 Edg/77.0.235.9

Edge18 User-Agent string:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362

Edge73 Stable (Android) User-Agent string:
Mozilla/5.0 (Linux; Android 10; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36 EdgA/42.0.4.3892

You’ll note that each of the Edge variants uses a different token at the end of the User-Agent string, but the string otherwise matches Chrome versions of the same build. Sites should almost never do anything with the Edge token information– treat Edge like Chrome. Failing to follow this advice almost always leads to bugs.

Sites are so bad about misusing the User-Agent header that Edge was forced to introduce a service-driven override list, which you can find at edge://compat/useragent. Alas, even that feature can cause problems in unusual cases. For testing, you can tell Edge to ignore the list by starting it thusly:

    msedge.exe --disable-domain-action-user-agent-override

The domain actions overrides originally were only used in Edge Stable and Beta channels but as of September 2025, there now exist User-Agent overrides that apply in all channels and which are not shown in edge://compat.

Upcoming Changes

Perhaps the biggest change with the move to the new Chromium-backed Microsoft Edge is a much faster pace of change in the Web Platform. You can keep an eye out on upstream changes using the ChromeStatus schedule page, and Microsoft’s list of site-impacting-changes.

Stay compatible out there!

-Eric

Protect Your Accounts with 2FA

You should enable “2-Step Verification” for logins to your Google account.

Google Authenticator is an app that runs on your iOS or Android phone and gives out 6 digit codes that must be entered when you log in on a device. This can’t really prevent phishing (because a phishing page will just ask you for a code from it and if you’re fooled, you’ll give it up) but it does prevent attacks if a bad guy has only your password. Authenticator is free and simple to use, and is supported by many sites, including GitHub. Microsoft offers a nearly identical Authenticator app too. How ToTP works.

YubiKeys (and similar) are small USB keys that you can configure your accounts to require. They are cheapish (~$18) and cannot be phished (even if you tap your key while on a phishing site, the attacker cannot use it due to how the crypto works). These are the best protection for your accounts (Googlers all use them) and are highly recommended for Chrome extension developers, journalists, activists, etc, etc.

Consider, for instance, this phishing email that this Chrome Extension developer received:

cwsphish

If the developer’s account were protected by a Yubikey, his credentials would be useless to a phisher because they would not have the required second factor necessary to log in and plant their malicious code in the developer’s extensions.

If the developer’s account were protected by a TOTP/Google Authenticator, it would require that the attacker collect the token value and be actively watching for victims such that the ephemeral token did not expire before they had a chance to replay it to the legitimate Google servers.

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