JavaScript Insertion: Performance Penalties Explained

October 28th, 2019

Javascript Insertion - penalties

No one would suggest that you shouldn’t defend your code, certainly not me. But, too often, that defense comes at a cost. Today, I’m going to discuss the significant performance penalties associated with hooking browser JavaScript APIs to defend against JavaScript based threats (such as Magecart etc.).

Loading Defensive Code First: Critical and Painful

When one builds a layer of defense, the security code should absolutely be the first resource loaded on the page. This code is either fetched as a remote script from a first or third party site OR is embedded inline in the page. Such scripts can be anywhere from 1500 to 2000 lines of JavaScript code (or about 50+ KB). The security script, in turn, securely fetches the configuration policies it enforces (unless the policies are embedded within the code itself - not the best of best practices). This causes the page to load slowly and, negatively affects user experience by delaying the receipt of the first meaningful byte (the effective TTFB, time to first byte) on the page by 50 kb (or 34+ ethernet packets).

Single Point of Failure: How API Hooking Gets in the Way of Everything

Secondly, the security code needs to hook onto EVERY possible JS API for DOM manipulation, local or remote/network I/O, local IPC etc. The security code at this point is running in a synchronous blocking mode, stalling the loading of any subsequent resources on that page. Nothing else runs or loads until this hooking is complete, effectively making this process a SPOF (Single Point of Failure). This code is very different from user experience monitoring scripts that can run asynchronously and can live with sampling. So, what is JS API hooking and why is it needed?

Hooking is a general programming technique to filter pre or post a function to debug and inspect function parameters and results. In the context of security, this inspection can be used to enforce policies and/or monitor the program behavior. In order to provide comprehensive coverage with no gaps, all possible functions for a particular behavior should be hooked.

As an example, by hooking onto document.createElement() but not document.write(). Javascript API surface is huge and it is very cumbersome to provide airtight coverage. If there are a 100+ JS APIs (especially when you have no knowledge about legitimate scripts or resources that load at run time) all API need to be hooked. If the 101st API is not hooked onto, it creates a security hole.

Further, the state of JS APIs across browsers is a complete mess given the number of APIs, the lack of support across the board, and the need for browser specific code. Researchers found that just the hooking operation itself adds an overhead of 8% (and in some cases, such as for eval(), the overhead could be over 10%). The cumulative overhead of inspecting or modifying parameters and results across 100s of APIs is detrimental to performance. For a lightweight inspection method the cumulative slowdown is ~ 20% and for full virtualization it could potentially be several times more.

API Hooking adversely impacts Javascript optimization techniques (e.g. JITing)

Professor Somesh Jha, an expert in compiler optimizations, at University of Wisconsin, Madison, points out that such API hooking completely messes with the JIT (Just in Time) optimizations in most browsers. Almost all browsers (except Chrome) stop "Jitting" once the “to-be-optimized” code patterns get invalidated. (For a primer on JIT, click here). As per another leading expert in this field, “Wrapping APIs that take callbacks means you have to wrap the callbacks: if you use the same wrapper, that thing is megamorphic at best, but more likely it doesn’t get optimized. Changing prototypes of objects - this is super bad for some of the globals, array being the most performance sensitive.”

The ultimate proof of these performance penalties comes from measurement tools (Lighthouse, New Relic, Chrome Dev tools, JITProf etc.) and real metrics. Performance issues caused by Javascript API hooking for security impacts the following commonly used metrics:

Even as API hooking degrades performance and negatively impacts user experience, the level of security your users get is insufficient. We’ll discuss the weakness of that security in an upcoming blog.



Sanjay Sawhney, Co-Founder and VP of Engineering

Written by Sanjay Sawhney, Co-Founder and VP of Engineering

Sanjay Sawhney is the co-founder and VP of Engineering of Tala. Sanjay is an experienced, engineering leader, technologist and entrepreneur who has worked for 25+ years in various engineering capacities in both well-established companies as well as startups. Most recently, he spent 9 years at Symantec managing Symantec Research Labs, one of the key innovation engines of the company. Prior to joining Symantec, he co-founded two companies and led their engineering – Neoscale Systems, a data encryption company, and Ukiah Software, a network security company. Earlier in his career, he has worked in various engineering positions at Novell. Sanjay received a B.Tech. in Electrical Engineering from Indian Institute of Technology, Delhi, and an M.S. in Computer Science from University of California, Santa Barbara.