How to Hook and Hide JavaScript APIs to Thwart XSS Attacks
This article explores practical techniques for intercepting and protecting JavaScript APIs—such as setAttribute—using MutationObserver, API hooks, random token naming, property hiding, and recursive iframe monitoring to build a resilient front‑end defense against XSS and other injection attacks.
Overview
The author experiments with module interception on the web page, distinguishing static modules scanned by MutationObserver and dynamic modules intercepted via API hooks. The core idea of a hook is to execute custom code before the original function without modifying the original program, creating a chain‑call pattern that works from low‑level instruction interception up to aspect‑oriented programming.
Hooking setAttribute
A minimal hook is demonstrated by overriding Element.prototype.setAttribute. The original function is saved in raw_fn, the new implementation checks for suspicious script sources, prompts the user, and finally forwards the call.
// Save original
var raw_fn = Element.prototype.setAttribute;
// Override
Element.prototype.setAttribute = function(name, value) {
if (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {
if (/xss/.test(value)) {
if (confirm('Attempt to load suspicious module:
' + value + '
Block?')) {
return;
}
}
}
raw_fn.apply(this, arguments);
};
// Test
var el = document.createElement('script');
el.setAttribute('SRC', 'http://www.etherdream.com/xss/alert.js');
document.body.appendChild(el);Hook Leakage and Bypass
Exposing raw_fn as a global variable allows an attacker to bypass the hook by calling the original function directly. Even rewriting Function.prototype.apply can leak the saved reference, demonstrating that any overridden global method can become an attack surface.
Function.prototype.apply = function() {
console.log('Got original interface:', this);
};
document.body.setAttribute('a', 1);An image illustrating the leak:
Concealing the Original Interface
To avoid global exposure, the original method can be renamed with a unique token and stored as a non‑enumerable property using Object.defineProperty. Random tokens make static analysis harder.
(function() {
var token = '$' + Math.random();
Element.prototype[token] = Element.prototype.setAttribute;
Element.prototype.setAttribute = function(name, value) {
this[token](name, value);
};
})();Further hiding is achieved by setting enumerable: false on the token property, making it invisible to typical enumeration loops.
Object.defineProperty(Element.prototype, token, {
value: Element.prototype.setAttribute,
enumerable: false
});Locking call and apply
Since many bypasses rely on call or apply, the article shows how to make these methods non‑writable and non‑configurable, effectively freezing them.
Object.defineProperty(Function.prototype, 'call', {
value: Function.prototype.call,
writable: false,
configurable: false,
enumerable: true
});
Object.defineProperty(Function.prototype, 'apply', {
value: Function.prototype.apply,
writable: false,
configurable: false,
enumerable: true
});Reflecting Into New Frames
Attackers can create isolated iframe environments where the original hooks are absent. To protect these, the author installs a monitoring system that watches for newly inserted IFRAME elements using DOMNodeInserted (or MutationObserver) and injects the same hook into the iframe’s contentWindow.
function installHook(win) {
var raw_fn = win.Element.prototype.setAttribute;
win.Element.prototype.setAttribute = function(name, value) {
alert(name);
raw_fn.apply(this, arguments);
};
win.document.addEventListener('DOMNodeInserted', function(e) {
if (e.target.tagName == 'IFRAME') {
installHook(e.target.contentWindow);
}
}, true);
}
installHook(window);This recursive installation ensures that even nested iframes receive the protective hook.
Limitations and Reverse Control
The article acknowledges that determined attackers can still bypass defenses by rewriting other native methods (e.g., RegExp.prototype.test, String.prototype.toUpperCase) or by injecting scripts via srcdoc or document.write. It suggests monitoring document.write after DOMContentLoaded and disabling rarely used properties like srcdoc when possible.
Conclusion
While the presented hooking techniques significantly raise the bar against XSS and similar attacks, the author warns that no single method is foolproof; continuous updates and layered defenses are required to keep up with evolving exploitation tactics.
Baidu Tech Salon
Baidu Tech Salon, organized by Baidu's Technology Management Department, is a monthly offline event that shares cutting‑edge tech trends from Baidu and the industry, providing a free platform for mid‑to‑senior engineers to exchange ideas.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
