Google Chrome–Two(ish) Years In

My first year (2016) on Chrome was both exciting and challenging. Beyond the expected firehose of new things to learn, and foreseen challenges (a second son!), there were unforeseen challenges, like working as a SWE instead of a Developer Advocate, and a long illness.

Overall, my second year (2017) on Chrome was a bit smoother– I was still learning a ton every day, but I finally started to make non-trivial contributions as a developer.

I started to develop a sense of direction when navigating the millions of lines of code that make up Chrome’s multi-process architecture, and did the development work for HTTPBad Phase 2. Building that feature involved sending messages from Blink through Mojo to the Browser process and triggering UI updates, collecting metrics and rolling out the feature gradually via a Chrome Field Trial. Shipping the change on iOS (where Chrome must use WkWebView rather than Blink) was especially tricky as the architecture, toolchain, and testing are all quite different.

I briefly took ownership of maintaining Chrome’s HSTS Preload list and processes and worked with domain registry fTLD in getting the .bank and .insurance top-level-domains added to the preload list. Getting TLDs preloaded is a huge win for security and performance, and hopefully we’ll see more TLDs joining in the near future.

The single biggest productivity improvement of the year was moving from building locally to using Goma for dramatically faster builds. With the benefit of hindsight, I should’ve done this in 2016 and failing to do so was one of the biggest mistakes I’ve made.

Reproducing, reducing, and triaging security issues remained my favorite thing to do, and I got better at identifying which of Chrome’s many experts was best suited to investigate and develop patches. I also made a bunch of updates to Chrome’s public Security FAQ and other documentation, and published some notes on my research into Chrome’s XSS Auditor. I had the chance to do the pre-launch security review for a number of cool features around the product, and work with other Google teams on a number of HTTPS-related issues. I filed 161 issues.

The only constant when working on Chrome is change, and I spent a lot of time keeping up with changes in everything from architecture (Site Isolation!) to tooling, including the move from Reitveld to Gerrit for code review. I also kept the extensions I’ve developed up-to-date with changes and bugfixes, and code-reviewed a few publicly-available extensions, both good and evil.

Speaking of changes…

I’ve learned a ton over the last two years and I’m grateful for having had the opportunity to help keep users safe and work with such a talented group of passionate engineers. I still get dizzy when I think about the size and skillset of the Chrome team, and I’m super-excited about the improvements coming to Chrome in the near future. While progress is never as fast as I’d like, I’m proud to see the real-world web moving toward a more encrypted, secure, and powerful future.

May 29th will be my last day on the Chrome team and at Google. Starting in June, I’ll be heading back to Program Management, working together with a lot of old friends.

browsers, security

SSLVersionMin Policy returns to Chrome 66

Chrome 66, releasing to stable this week, again supports the SSLVersionMin policy that enables administrators to control the minimum version of TLS that Chrome is willing to negotiate with a server.

If this policy is in effect and configured to permit, say, only TLS/1.2+ connections, attempting to connect to a site that only supports TLS/1.0 will result in an error page with the status code ERR_SSL_VERSION_OR_CIPHER_MISMATCH.

This policy existed until Chrome 52 and was brought back for Chrome 66 and later. It is therefore possible that your administrators configured it long ago, forgot about the setting, and will be impacted again with Chrome 66’s rollout.


browsers, security

Chrome 59 on Mac and TeletexString Fields

Update: This change ended up getting backed out, after it was discovered that it impacted smartcard authentication. Thanks for self-hosting Chrome Dev builds, IT teams!

A change quietly went into Chrome 59 that may impact your certificates if they contain non-ASCII characters in a TeletexString field. Specifically, these certificates will fail to validate on Mac, resulting in either a ERR_SSL_SERVER_CERT_BAD_FORMAT error for server certificates or a ERR_BAD_SSL_CLIENT_AUTH_CERT error for client certificates. The change that rejects such certificates is presently only in the Mac version of Chrome, but it will eventually make its way to other platforms.

You can see whether your certificates are using teletexStrings using an ASN.1 decoder program, like this one. Simply upload the .CER file, and look for the TeletexString type in the output. If you find any such fields that contain non-ASCII characters, the certificate is impacted:

Non-ASCII character in string

Background: Certificates are encoded using a general-purpose data encoding scheme called ASN.1. ASN.1 specifies encoding rules, and strings may be encoded using any of a number of different data types (teletexString, printableString, universalString, utf8String, bmpString). Due to the complexity and underspecified nature of the TeletexString, as well as the old practice of shoving Latin1 strings in fields marked as TeletexString, the Chrome change takes a conservative approach to handling TeletexString, only allowing the ASCII subset. utf8String is a well-specified and well-supported standard and should be used in place of the obsolete teletexString type.

To correct the problem with the certificate, regenerate it using UTF8String fields to store non-ASCII data.

-Eric Lawrence

dev, security

Chrome Deprecates Subject CN Matching

If you’re using a Self-Signed certificate for your HTTPS server, a deprecation coming to Chrome may affect your workflow.

Chrome 58 will require that certificates specify the hostname(s) to which they apply in the SubjectAltName field; values in the Subject field will be ignored. This follows a similar change in Firefox 48. If impacted, you’ll see something like this blocking page as you load your HTTPS site:

NET::ERR_CERT_COMMON_NAME_INVALID blocking page in Chrome

NET::ERR_CERT_COMMON_NAME_INVALID is an unfortunate error code, insofar as all common names are now ignored. Chrome is working to improve the debuggability of this experience, via:

Update: Both of these have landed. Chrome now shows [missing_subjectAltName] in the details on the Certificate Error page, and a Subject Alternative Name Missing warning in the Security panel of the Developer tools.

Notably, Windows’ ancient makecert.exe utility cannot set the SubjectAltName field in certificates, which means that if you’re using it to generate your self-signed certificates, you need to stop. Instead, users of modern Windows can use the New-SelfSignedCertificate command in PowerShell.

New-SelfSignedCertificate -DnsName "", "" -CertStoreLocation "cert:\CurrentUser\My"

Using openssl for self-signed certificate generation? See

This new restriction may also impact users of very old versions of Fiddler (or FiddlerCore), or users who have configured Fiddler to use MakeCert for whatever reason. Fortunately, Fiddler offers a number of different certificate generators, so you just need to make a small configuration change. To switch away from MakeCert, click Tools > Fiddler Options > HTTPS and click the “Certificates generated by MakeCert engine” link. Change the dropdown to CertEnroll and click OK. Click Actions > Reset All Certificates and restart Fiddler.


If you’re building an application atop FiddlerCore, you’ll need to make sure you’re not using makecert; see the end of this post for help.

-Eric Lawrence

PS: There’s also a EnableCommonNameFallbackForLocalAnchors policy. You shouldn’t use it and you should just fix your certificates, or they’ll break when it’s removed in Chrome 65 or earlier.

PS: There’s now official documentation on this topic.

browsers, dev

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.

Landmines around filename case-sensitivity

chrome-extension URLs are case-sensitive only on Linux and CrOS which means that you can easily write an extension that works correctly on Mac/Win and fails on CrOS and Linux; on CrOS, the extension might be marked as corrupt while on Linux the request will just fail (probably breaking the extension).

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.

browsers, dev

Things I’ve Learned in my first weeks on Chrome

This is a stub post which will be updated periodically.

It would be impossible to summarize how much I’ve learned in the last six weeks working at Google, but it’s easy to throw together some references to the most interesting and accessible things I’ve learned. So that’s this post.

Developing Chrome

Searching the code is trivial. You don’t need to know C++ to read C++. And if you can write C++, the process of adding new code to Chrome isn’t too crazy.

Creating bugs is easy:

Developing Chrome extensions is easy and approximately 5% as hard as building IE extensions.

Using Chrome

PM’ing at Microsoft was all about deleting email. Surviving at Google is largely an exercise in tab management, since nearly everything is a web page. QuickTabs allows you to find that tab you lost with a searchable most-recently-used list.

You can CTRL+Click *multiple* Chrome tabs and drag your selections out into a new window (unselected tabs temporarily dim). Use SHIFT+Click if you’d prefer to select a range of tabs.

Hit SHIFT+DEL when focused on unwanted entries in the omnibox (addressbar) dropdown to get rid of them.

Want to peek under the hood? Load chrome://chrome-urls to see all of the magic URLs that Chrome supports for examining and controlling its state, caches, etc. For instance, the ability to view network events and export them to a JSON log file (see chrome://net-internals), later importing those events to review them using the same UI is really cool. Other cool pages are chrome://tracing, chrome://crashes, and chrome://plugins/.

Chrome uses a powerful system of experimental field trials; your Chrome instance might behave differently than everyone else’s.

browsers, security

My Next Adventure

Back in 2004, I couldn’t get the tiny IE team interested in fixing caching bugs that were causing my team’s website to break in bizarre and unpredictable ways. I figured I’d hop over there, fix some bugs, and move along. I quickly realized that I was hopelessly in love with browsers in general and security in particular. The hours were long, the problems were immense, but it was easy to make a big difference.

After eight years on the IE team, mostly working in Security, I felt like I’d crafted my dream job. I got to work with smart people every day, and help protect the browsing public from some very bad guys. It was very difficult to leave, but Telerik offered me a different dream job—building my side-project, Fiddler, on a full-time basis.

I spent three years here, doing important work on Fiddler to help take it from a side-project to a professional-caliber tool with the features and polish that users expect. And I’ve had a blast.


I’ve missed working on browsers. And on security. While Fiddler has kept me close to that scene, it wasn’t quite close enough. As one of my old leads on the IE team once observed: “No matter what you add to Fiddler, your work on a browser used by almost a billion people will always have a greater impact.”

In the midst of this nostalgic longing, a former colleague posted the following tweet:

Tweet: Join the Chrome Security team

Casual conversations were had. Then more serious conversations. Then interviews. And more interviews.

Philip Su, a brilliant guy whose tech talk inspired me to apply at Microsoft once said: “The team you want to join is the one that’s hard to get into.

Every single person I talked to was smarter than I was. It was crazy-intimidating. And inspiring.

Tweet: Wanna grow? Be the dumbest guy in the room

For my first few years on IE, there was ample speculation that Google would release a browser, presumably some slick skinning of Firefox with all the Google services bolted on. Then Chrome shipped and it was so much more interesting. So many things were done “right” from day 1, and the pace of evolution was amazing. And it wasn’t just the technical guts– I was astonished to see things like the Chrome comic book, which explained things like process isolation and integrity levels in a way that mere mortals could understand. Chrome continued to evolve and grow to take on its role as a platform; ChromeOS now powers a third of the machines in my house. The Chrome team is driving the security of the web as a whole.

I want users to win. To achieve that, I want the web to win, and I want to make life harder for the bad guys every day.

I’m thrilled, excited, honored, and more than a little intimidated to be joining the Chrome Security team on January 4th 2016.

Let the firehose-drinking begin!

Boy being knocked over by a stream of water from a firehose directed at his face