Security UI in Chrome

The combined address box and search bar at the top of the Chrome window is called the omnibox. The icon and optional verbose state text adjacent to that icon are collectively known as the Security Chip:


The security chip can render in a number of states, depending on the status of the page:

image Secure – Shown for HTTPS pages that were securely delivered using a valid certificate and not compromised by mixed content or other problems.
image Secure, Extended Validation – Shown for Secure pages whose certificate indicates that Extended Validation was performed.
image Neutral – Shown for HTTP pages, as well as Chrome’s built-in pages, like chrome://extensions, as well as pages containing passive mixed content, or delivered using a policy-allowed SHA-1 certificates.
image Not Secure – Shown for HTTP pages that contain a password or credit card input field. Learn more.
image Not Secure (Red) – What Chrome will eventually show for all HTTP pages. You can configure a flag (chrome://flags/#mark-non-secure-as) to Always mark HTTP as actively dangerous today to get this experience early.
image Not Secure, Certificate Error – Shown when a site has a major problem with its certificate (e.g. it’s expired).
image Dangerous – Shown when Google Safe Browsing has identified this page as a source of malware or phishing attacks.

The flyout that appears when you click the security chip is called PageInfo, it shows the security status of the page and the permissions assigned to the origin website:


The text atop the pageinfo flyout explains the security state of the page:

image imageimage mixedexpired

Clicking the Learn More link on the flyout for valid HTTPS sites once opened the Chrome Developer Tools’ Security Panel, but now it goes to a Help article. To learn more about the HTTPS state of the page, instead press F12 and select the Security Panel:


The View certificate button opens the Windows certificate viewer and displays the current origin’s certificate. Reload the page with the Developer Tools open to see all of the secure origins in the Secure Origins List; selecting any origin allows you to view information about the connection and examine its certificate.


The floating grey box at the bottom of the Chrome window that appears when you over over a link is called the status bubble. It is not a security surface and, like IE’s status bar, it is easily spoofed.


Navigation to sites with severe security problems is blocked by an interstitial page.


(A list of interstitial pages in Chrome can be found at chrome://interstitials/ ).

Clicking on the error code (e.g. ERR_CERT_AUTHORITY_INVALID in the screenshot below) will show more information about the certificate of the blocked site:


Clicking the Advanced link shows more information, and in some cases will show an override link that allows you to disregard the security protection and proceed to the site anyway.


If a site uses HTTP Strict Transport Security, the Proceed link is hidden and the user has no visible option to proceed.


In current versions of Chrome, the user can type a fixed string (sometimes referred to as a Konami code) to bypass HSTS, but this option is deliberately undocumented and slated for removal from Chrome.

If a HTTPS problem is sufficiently bad, the network stack will not connect to the site and will show a network error page instead.



PS: There exists a developer reference to Chrome Security UI across platforms, but it’s somewhat outdated.

Security UI in Chrome

Useful Resources when Developing Chrome Extensions

I’ve built a handful of Chrome extensions this year, and I wrote up some of what I learned in a post back in March. Since then, I’ve found two more tricks that have proved useful.

First, the Chrome Canary channel includes a handy extension error console to quickly expose extension errors.

Simply open the chrome://extensions page and an Errors link will be available for side-loaded extensions (“Load Unpacked Extension”):

Errors link on Chrome://extensions

Error console

This error console can be enabled in other channels (dev/beta/stable) by launching with the --error-console command line argument.

Next, you’ll often end up with both a public version of your extension and a side-loaded developer build installed at the same time. If their Action icons are the same, it’s easy to get confused about which version is which.

To help clarify which is the development version, you can easily add a Dev badge to the icon of the developer build:
Dev badge



The code to add to your background.js file is straightforward and works even if the background page is not persistent:

 // Our background page isn't persistent, so subscribe to events.
 chrome.runtime.onStartup.addListener(()=> { init(); });
 // onInstalled fires when user uses chrome://extensions page to reload
 chrome.runtime.onInstalled.addListener(() => { init(); });

 function init()
   // Add Badge notification if this is a dev-install (o)=>{
     if (o.installType === "development") {
       chrome.browserAction.setBadgeText( {text: "dev"} );

Finally, as mentioned in my last post, the Chrome Extension Source Viewer remains invaluable for quickly peeking at the code of existing extensions in the store.

I’ve had more fun writing Chrome extensions than anything else I’ve built this year. If you haven’t tried building an extension yet, I encourage you to try it out. Best of all, your new Chrome extension development skills will readily transfer to building extensions to Opera, Firefox, and Microsoft Edge.


Useful Resources when Developing Chrome Extensions

Bolstering HTTPS Security

When #MovingToHTTPS, the first step is to obtain the necessary certificates for your domains and enable HTTPS on your webserver. After your site is fully HTTPS, there are some other configuration changes you should consider to further enhance the site’s security.

Validate Basic Configuration

First, use SSLLab’s Server Test  to ensure that your existing HTTPS settings (cipher suites, etc) are configured well.


SECURE Cookies

After your site is fully secure, all of your cookies should be set with the SECURE attribute to prevent them from ever leaking over a non-secure channel. The (confusingly-named) HTTPOnly attribute should also be set on each cookie that doesn’t need to be accessible from JavaScript.

    Set-Cookie: SESSIONID=b12321ECLLDGH; secure; path=/

Strict Transport Security (HSTS)

Next, consider enabling HTTP Strict Transport Security. By sending a HSTS header from your HTTPS pages, you can ensure that all visitors navigate to your site via HTTPS, even if they accidentally type “http://” or follow an outdated, non-secure link.

HSTS also ensures that, if a network attacker were to try to intercept traffic to your HTTPS site using an invalid certificate, a non-savvy user can’t wreck their security by clicking through the browser’s certificate error page.

After you’ve tested out a short-lived HSTS declaration, validated that there are no problems, and ramped it up to a long-duration declaration (e.g. one year), you should consider requesting that browsers pre-load your declaration to prevent any “bootstrap” threats (whereby a user isn’t protected on their very first visit to your site).

    Strict-Transport-Security: max-age=631138519; includeSubDomains; preload

The includeSubdomains attribute indicates that all subdomains of the current page’s domain must also be secured with HTTPS. This is a powerful feature that helps protect cookies (which have weird scoping rules) but it is also probably the most common source of problems because site owners may “forget” about a legacy non-secure subdomain when they first enable this attribute.

Strict-Transport-Security is supported by most browsers.

Certificate Authority Authorization (CAA)

Certificate Authority Authorization (supported by LetsEncrypt) allows a domain owner to specify which Certificate Authorities should be allowed to issue certificates for the domain. All CAA-compliant certificate authorities should refuse to issue a certificate unless they are the CA of record for the target site. This helps reduce the threat of a bad guy tricking a Certificate Authority into issuing a phony certificate for your site.

The CAA rule is stored as a DNS resource record of type 257. You can view a domain’s CAA rule using a DNS lookup service. For instance, this record for means that only Symantec’s Certificate Authority may issue certificates for that host:


Configuration of CAA rules is pretty straightforward if you have control of your DNS records.

Public Key Pinning (HPKP)

Unfortunately, many CAs have made mistakes over the years (e.g. DigiNotar, India CCA, CNNIC CA, ANSSI CA) and the fact that browsers trust so many different certificate authorities presents a large attack surface.

To further reduce the attack surface from sloppy or compromised certificate authorities, you can enable Public Key Pinning. Like HSTS, HPKP rules are sent as HTTPS response headers and cached on the client. Each domain’s HPKP rule contains a list of Public Key hashes that must be found within the certificate chain presented by the server. If the server presents a certificate and none of the specified hashes are found in that certificate’s chain, the certificate is rejected and (typically) a failure report is sent to the owner for investigation.

Public Key Pinning powerful feature, and sites must adopt it carefully—if a site carelessly sends a long-life HPKP header, it could deny users access to the site for the duration of the HPKP rule.

    Public-Key-Pins: pin-sha256=”8Rw90Ej3Ttt8RRkrg+WYDS9n7IS03bk5bjP/UXPtaY8=”;
max-age=600; includeSubdomains;

Free services like can be used to collect HPKP violation reports.

HPKP is supported by some browsers.

Certificate Transparency

Certificate Transparency is a scheme where each CA must publicly record each certificate it has issued to a public, auditable log. This log can be monitored by site and brand owners for any unexpected certificates, helping expose fraud and phony domains that might have otherwise gone unnoticed.

Site owners who want a lightweight way to look for all CT-logged certificates for their domains can use the Certificate Transparency Lookup Tool. Expect that other tools will become available to allow site owners to subscribe to notifications about certificates of interest.

Since 2015, Chrome has required that all EV certificates issued be logged in CT logs. Future browsers will almost certainly offer a way for a site to indicate to visiting browsers that it should Expect or Require that the received certificate be found in CT logs, reporting or blocking the connection if it is not.


Thanks for your help in securing the web!


Bolstering HTTPS Security

Building the moarTLS Analyzer

I’m passionate about building tools that help developers and testers discover, analyze, and fix problems with their sites.

Some of the first code I ever released was a set of trivial JavaScript-based browser extensions for IE5. I later used the more powerful COM-based extensibility model to hack together some add-ons that would log ActiveX controls and perform other tasks as you browsed. But the IE COM extensibility model was extremely hard to use safely, and I never released any significant extensions based on it.

Later, I built a simple Firefox extension based on their XUL Overlay extensibility model to better-integrate Fiddler with Firefox, but this extension recently stopped working as Mozilla begins to retire that older extensibility model in favor of a new one.

Having joined the Chrome team, I was excited to see how difficult it would be to build extensions using the Chrome model which is conceptually quite a bit different than both the old IE and Firefox models. Both Microsoft Edge (IE’s successor) and Firefox are adopting the Chrome model for their new extensions, so I figured the time would be well-spent.

I haven’t ever coded anything of consequence using JavaScript, HTML, and CSS, so I expected that the learning curve would be pretty steep.

It wasn’t.

Sitting on the couch with my older son and an iPad a few weeks ago, I idly Googled for “Create Chrome Extension.” One of the first hits was John Sonmez’s article “Create a Chrome Extension in 10 Minutes Flat.” I’m currently reading a book he wrote and I like his writing style, so with a fair amount of skepticism, I opened the article.

Wow, that looks easy.


After my kids went to bed that night, I banged out my first trivial Chrome extension. After suffering from nearly non-existent documentation of IE’s extension models, and largely outdated and confusing docs for Mozilla’s old model, I was surprised and delighted to discover that Chrome has great documentation for building extensions, including a simple tutorial and developer’s guide

Beyond that, there’s a magically wonderful Chrome Extension Source Viewer that allows you to easily peruse the source code of every extension on the Chrome Web Store:

CRX Viewer

CRX Source

Over the next few weeks, I built my moarTLS Analyzer extension, mostly between the hours of 11pm and 2am– peak programmer hours in my youth, but that was long ago.

I pulled out some old JavaScript and CSS books I’d always been meaning to read, and giggled with glee at the joy of building for a modern browser where all of the legacy hacks these books spilled so much ink over were no longer needed.

I found a few minor omissions from the Chrome documentation (bugs submitted) but on the whole I never really got stuck.

My Code

You can install the extension from the Chrome Web Store. The extension is on GitHub; have a look at the source and feel free to report bugs.

The code is simple and you can read it all in less than five minutes:

Code Map

The images folder contains the images used in the toolbar and the report UI. The manifest.json file defines the extension’s name, icons, permissions, and browser compatibility. The popup.{css|html|js} files implement the flyout report UI. When invoked, the flyout uses the executeScript and insertCSS APIs to add the injected.{css|js} files to each frame in the currently loaded page. The script sends a message back to the popup to tattle on any non-secure links it finds. The background.js file watches for File Downloads and shows an alert() if any non-secure downloads occur. The options.{css|html|js} files implement the settings block that control the extension’s settings.


Things I Learned

allFrames Isn’t Always

When you call chrome.tabs.executeScript with allFrames: true, your script will only be injected into same-origin subframes if your manifest.json only specifies the activeTab permission. To have it inject your script into cross-origin subframes, you must declare the <all_urls> permission. This is unfortunate, but entirely logical for security reasons. Declaring the all_urls permission results in a somewhat scary permissions dialog when the extension is installed:

Permissions prompt

The Shadow DOM Hides Things

When I was testing the extension, I noticed that it wasn’t working properly on the ChromeStatus site. A quick peek at the Developer Tools revealed that my call to document.querySelectorAll(“a[href]”) wasn’t turning up any anchor elements nested inside the #shadow-root nodes.

#shadow-root node

These nodes are part of the Shadow DOM, a technology that allows building web pages containing web components—encapsulated blocks of markup written in HTML and CSS. By default, the internal markup of these nodes is invisible to normal DOM APIs like getElementsByClassname.

Fortunately, this was easy to fix. While deprecated in CSS, the /deep/ selector can still be used by querySelectorAll, and changing my code to document.querySelectorAll(“* /deep/ a[href]”); allowed enumeration of the links in the Shadow DOM.


The Downloads API Is Limited

The chrome.downloads API offers a lot of functionality, but not the one key bit I wanted—access to the raw file after the download is complete. Enabling moarTLS to warn users when a file download came from HTTP was easy, but I wanted to also automatically compute the hash of the downloaded file and display it for examination (since some sites still don’t sign files but they do publish their hashes).

Unfortunately, it looks like the only way to achieve this is to have a native platform installer that installs an executable, and have the Chrome extension use nativeMessaging to invoke that executable on the file. I’ll try that soon… I’m probably going to try to write the native portion in Go so that it will run on Windows, Linux, and OS X.


Publishing your Extension is Easy

I figured getting my add-on listed on the Chrome Web Store would be complicated. It’s not. It costs $5 to get a developer account. Surprisingly, you don’t use the Pack extension command in the chrome://extensions tab—you instead just ZIP up the folder containing your manifest and other files. Be sure to omit unneeded files like unit tests, unused fonts, etc, and optimize your images. After you’ve got that ZIP, you simply upload it to the store. You’ll need to use the WebUI to upload a number of screenshots and other metadata about the extension, but it’ll be live for everyone to download shortly thereafter.

Updates are simple—just upload a new ZIP file, tweak any metadata, and wait about an hour for the update to start getting deployed to users.


Firefox, Edge, and Opera

After publishing my extension yesterday, two interesting things happened. First, someone said “I’ll use it when it runs in Firefox” and second, Microsoft released the first build of Edge with support for browser extensions. Last night, decided to look at what’s involved in porting moarTLS to Firefox and Edge.

For Firefox, it’s actually pretty straightforward and took about twenty minutes (coding without a mouse!):

Firefox with moarTLS


I grabbed the Nightly build of Firefox and  popped open their instructions on Porting Extensions from Chrome and Loading Unpacked Extensions from Disk. I had to make only a few tiny tweaks to get my extension running in Firefox:

1. The manifest needs an applications object:

  “applications”: {
    “gecko”: {
        “id”: “”,
        “strict_min_version”: “47.0”

2. The chrome object is named browser instead. You can resolve this with the following code at the top of your script:  if (!chrome) chrome = browser || msBrowser || chrome; The chrome object is also defined, so it’s not clear there’s any value in preferring one over the other

3. It appears the /deep/ selector doesn’t work.

4. Styles using the -webkit- prefix need either the -moz- prefix or need to gracefully fall back.

5. The storage API is not available to content scripts yet.


Mozilla tracks progress of their implementation on


Getting the extension running in Microsoft Edge wasn’t possible yet. At first, even after adding:

  “minimum_edge_version”: “33.14281.1000.0”,

…to the manifest, the extension wouldn’t load at all. SysInternals’ Process Monitor revealed that the browser process was getting Access Denied when trying to read manifest.json. I suspect the reason is that Microsoft hasn’t yet hooked up the plumbing that allows read access out of the sandboxed AppContainer—this explains why Microsoft’s three demo extensions are unpacked by an executable instead of a plain ZIP file—the executable probably calls ICALCS.exe to set up the permissions on the folder to allow read from the sandbox. I tested this theory by allowing their installer to unpack one of the demo extensions, then I ripped out all of their files in that folder and replaced them with my own and it was loaded.

The extension still doesn’t run properly however; none of Microsoft’s three demos uses a browser_action with a default default_popup so I’m guessing that maybe they haven’t hooked up this capability yet. I’m hassling the Edge team on Twitter. :)

I haven’t tried building an Opera Extension yet, but I suspect my Chrome Extension will probably work almost without modification.

Building the moarTLS Analyzer

Putting Users First

When I worked on Internet Explorer, the team was proud of the fact that we could claim to be more aligned with our users’ goals than either of our major competitors (both of whom were funded almost entirely by advertising). IE, the story went, was paid for by users who purchased Windows, and thus our true customers were our users, not advertisers.

Over eight years on the team, there were very few instances where a decision was made that seemed to violate that “Users first, always” mantra (“Suggested Sites” being one noteworthy exception).

I was most proud of the work done around the IE Search Provider APIs, which made it easy for IE users to use the search engine of their choice, even though we knew that users’ choice would often not be Microsoft’s offering.

Add Search Provider (showing make default)

Last year, I was disappointed to see that Microsoft started removal of the Search Provider APIs, first deprecating them into legacy document modes, and next omitting them from the new Microsoft Edge browser. The result was that users had to follow a convoluted set of steps to add search providers for DuckDuckGo, Google, Wikipedia, etc. As a developer who loved using custom search providers for topic-specific searches on MSDN, StackOverflow, Amazon, and the like, I was really disappointed to see this change. I was only heartened to see this user-hostile change hadn’t been backported to earlier versions of Internet Explorer.

I recently upgraded Windows 10 to build 11082. Upon opening Internet Explorer 11, the following modal dialog box appeared:


I like to think that this dialog would never have shipped in the years I worked on IE. First, and most glaringly, the default option is to hijack the user’s search provider and homepage to Microsoft-owned properties. Next, the dialog box tries to justify this hijacking by implying that IE didn’t previously protect these settings (false) and that “websites” could “silently change” these settings (false). Clicking “Click here to learn more” takes the user to a page (delivered insecurely) which vaguely hand-waves about the threat of local malware, and says nothing about misbehaving websites.

So, if Microsoft is now “protecting” the settings they’ve just changed, how are they doing it?

Let’s have a look at the new version of that Add Search Provider dialog box we saw earlier. See what’s missing?

Add Search Provider (missing "make default")

That’s right—the choice to change your default search engine has been removed. (The option is now buried in a subtab of a subdialog of the Internet Options dialog.)



-Eric Lawrence

Putting Users First

DLL Hijacking Just Won’t Die

The folks that build the NSIS Installer have released updates to mitigate a serious security bug related to DLL loading. (v2.5 and v3.0b3 include the fixes).

To make a long and complicated story short, a bad guy who exploits this vulnerability places a malicious DLL into your browser’s Downloads folder, then waits. When you run an installer built by an earlier version of NSIS from that folder, the elevation prompt (assuming it runs at admin) shows the legitimate installer’s signature asking you for permission to run the installer. After you grant permission, the victim installer loads the malicious DLL which runs its malicious code with the installer’s permissions. And then it’s not your computer anymore.

So, how does the attacker get the malicious DLL into the browser’s Downloads folder? Surely that must involve some crazy hacking or social engineering, right?

Nah. The bad guy just navigates a frame of your browser to the DLL of his choice and, if you’re on Chrome orMicrosoft Edge, the DLL is dropped in the Downloads folder without even asking. Oops.

It’s trivial to simulate this attack to find vulnerable installers, even as a total noob.

1. Simply click this link which downloads a fake DLL (a simple text file containing the string ThisIsNotADLL).

2.Then download and run an installer built by an old version of NSIS:


3. Accept the elevation prompt:


4. Boom… watch Windows try to load your fake DLL as code:


Of course, a real bad guy isn’t going to be nice enough to use a fake DLL, he’ll instead use a valid but malicious one containing code that runs during DLL load, silently rooting your computer or whatever other nefarious thing he’d like.

Around 80% of the installers in my \Downloads\ folder are vulnerable to this attack; half of those are built on NSIS and half are built using other technologies.

You can learn more about DLL Hijacking attacks here.

EXE Hijacking

I’ve found some otherwise-safe installers will perform unqualified calls to system utilities (e.g. to manipulate the firewall or registry, etc). If a bad guy plants an executable of the target name (e.g. “netsh.exe”) in the \Downloads\ folder, it could be executed without warning, much like the DLL hijacking attack.

Action Items

Here are my suggestions:

  1. If you build installers with NSIS, please upgrade immediately.
  2. If you build installers with other technology, test for this attack (version.dll, msi.dll, shfolder.dll, etc). This post suggests a more complete way of finding target filenames. Another post notes that InnoSetup is vulnerable.
  3. Read Microsoft’s guidance for mitigating this vulnerability.
  4. Ask your browser vendor what they’re doing to prevent attacks of this nature. If they offer a “Confirm downloads” setting, use it.

-Eric Lawrence

DLL Hijacking Just Won’t Die