MiddleCodeOccasionalNot answered yet
Fix the insecure postMessage handler so it validates the sender and renders safely
A widget receives profile data from its parent frame. This handler has two client-side security defects.
Constraints:
- only
https://app.example.commay drive this widget msg.nameis plain text supplied by the user
window.addEventListener('message', (event) => {
const msg = JSON.parse(event.data);
document.getElementById('greeting').innerHTML = 'Hi, ' + msg.name;
});
Find and fix the vulnerabilities.
Two defects. The handler never checks event.origin, so any window can drive it, and it writes user text into innerHTML, a parsing sink. Compare event.origin to https://app.example.com exactly and return on mismatch, then render with textContent.
- ✗Checking
event.sourceor an origin suffix instead of an exact origin match - ✗Escaping quote characters instead of switching off the parsing sink
- ✗Assuming a structured message channel makes the payload safe for
innerHTML
- →Why does returning early on an origin mismatch matter more than sanitising the value?
- →When would
textContentbe insufficient and Trusted Types be the better control?
Contents
The defects
- No sender validation. Any window that framed your widget can
postMessageto it. Without anevent.origincomparison, a foreign page controls the greeting content. - A parsing sink.
innerHTMLparses its string as markup, so user-controlledmsg.nameenters the DOM as HTML — that is DOM XSS.
The fix
const ALLOWED_ORIGIN = 'https://app.example.com';
window.addEventListener('message', (event) => {
if (event.origin !== ALLOWED_ORIGIN) return; // 1. exact match, early return
let msg;
try { msg = JSON.parse(event.data); } catch { return; }
document.getElementById('greeting').textContent = 'Hi, ' + msg.name; // 2. text sink
});
✅ The comparison is strict (!==), not a substring or suffix test — https://app.example.com.evil.tld fails it. ⚠️ Checking event.source instead of the origin is not a substitute: the source can be swapped by re-arranging windows.
Contents