Search code examples
javascriptsecuritydomwebsecurity

How to detect or prevent built-in browser functions from being replaced?


I noticed today that I can replace a sensitive built-in JS function like this:

async function _hackedEncrypt(algorithm, key, data) {
   console.log('hacked you!');
}

const subtle = global.crypto.subtle; // Assign to get around "read-only" error.
subtle.encrypt = _hackedEncrypt;

global.crypto.subtle.encrypt(); // 'Hacked you!' appears in console.

Yikes!

This exploit is so simple. Any of the thousands of dependencies (direct and transitive) in my web app could make this function reassignment. Note that my question isn't specific to Web Crypto - it's just one of the more dangerous targets for an attacker.

How can I either detect that the function has been reassigned or guarantee that I'm always calling the original browser implementation of it?


Solution

  • What you are describing is a subset of a broader class of attacks: Supply Chain Attacks.

    The basic idea is that I create a JS library that is useful and trivial, over time a ton of projects depend on it, then I insert a back door, next time they upgrade they're dependencies - I have access to their users' browsers.
    I can then steal passwords, credit card info, PII, etc. - basically digital skimming. Magecart is probably being the best known attack in the JS space. Probably the best known one in general is the Solarwinds attack.

    One solution is to roll your own. That is pretty much hopeless, because even though you might know what APIs you use, you don't know what your dependencies use, and how that changes.

    Another solution is Content Security Policy, which controls outgoing requests: a skimmer might have been able to put their code in your app, but they can't do anything useful if your content security policy doesn't let it issue requests to unknown hosts.

    There are also commercial solution. I'm aware of: