Slide 1

Slide 1 text

Security issues being discussed on Web Platforms Web プラットフォームで議論されているセキュリティ課題 Frontend Conference Fukuoka 2026 pixiv Inc. 森内建太 @petamoriken 2026.9.12

Slide 2

Slide 2 text

Can you find the issue? (CVE-2024-45389) // Get the element for this file (classic script) const script = document.currentScript; // Get the src of <script> and retrieve the JS base path const baseUrl = new URL(script.src).pathname; // Load and execute the related JS file import(`${baseUrl}related.js`) .then((moduleNS) => {/* */}); 2

Slide 3

Slide 3 text

Can you find the issue? (CVE-2024-45389) // Get the element for this file (classic script) const script = document.currentScript; // Check if script is actually a <script> element if (script.tagName.toUpperCase() !== "SCRIPT") { throw new Error("DOM clobbering!"); } // Get the src of <script> and retrieve the JS base path const baseUrl = new URL(script.src).pathname; // Load and execute the related JS file import(`${baseUrl}related.js`) 3 .then((moduleNS) => {/* */});

Slide 4

Slide 4 text

DOM Clobbering 4

Slide 5

Slide 5 text

DOM Clobbering FEC Fukuoka 2026 console.log(document.title); // "FEC Fukuoka 2026" 5

Slide 6

Slide 6 text

DOM Clobbering FEC Fukuoka 2026 console.log(document.title); // HTMLImageElement 6

Slide 7

Slide 7 text

XSS + DOM Clobbering 🤯 console.log(document.currentScript.src); // "evil.example.com" 7

Slide 8

Slide 8 text

To Resolve CVE-2024-45389 // Get the element for this file (classic script) const script = document.currentScript; // Check if script is actually a <script> element if (script.tagName.toUpperCase() !== "SCRIPT") { throw new Error("DOM clobbering!"); } // Get the src of <script> and retrieve the JS base path const baseUrl = new URL(script.src).pathname; // Load and execute the related JS file import(`${baseUrl}related.js`) 8 .then((moduleNS) => {/* */});

Slide 9

Slide 9 text

Preventing DOM Clobbering via Document-Policy (WIP) Document-Policy: override-builtins=?0 9

Slide 10

Slide 10 text

Security issues being discussed on Web Platforms Web プラットフォームで議論されているセキュリティ課題 Frontend Conference Fukuoka 2026 pixiv Inc. 森内建太 @petamoriken 2026.9.12

Slide 11

Slide 11 text

Table of Contents ● DOM Clobbering ● (Self-Introduction) ● Prototype Pollution ● Thenable (Promises/A+) ● TC39 Task Group 3: Security 11

Slide 12

Slide 12 text

Self-Introduction petamoriken pixiv WebDev engineer in Fukuoka 12 ● A Deno contributor ● An advocate for ES2025 Float16Array ● I like to keep up with Web Standard

Slide 13

Slide 13 text

Normative: Don't call well-known Symbol methods for RegExp on primitive values "a b c".split(" "); // ["a", "b", "c"] String.prototype[Symbol.split] = () => "hi!"; "a b c".split(" "); // "hi!" 13

Slide 14

Slide 14 text

I Contributed to V8 for the Normative 14

Slide 15

Slide 15 text

Prototype Pollution プロトタイプ汚染 15

Slide 16

Slide 16 text

What is the Prototype Chain? All objects have a [[Prototype]] internal slot, which is traversed when accessing properties [1, 2, 3].includes(2); [1, 2, 3] Array.prototype “includes” method 16 Object.prototype null

Slide 17

Slide 17 text

Prototype Pollution Convenient for polyfills, but allows arbitrary properties... const obj = { foo: 1 }; Object.prototype.bar = 2; console.log(obj.foo); // 1 console.log(obj.bar); // 2 17

Slide 18

Slide 18 text

Prototype Pollution It can break JSON.stringify 😢 Object.prototype.toJSON = () => "hi!"; JSON.stringify({ foo: 1 }); // "hi!" 18

Slide 19

Slide 19 text

Prototype Pollution Attack 💥 e.g. Overwriting Object.prototype via {}.constructor.prototype const payload = { constructor: { prototype: { isAdmin: true, }, }, }; unsafeMerge(obj, payload); 19 Object.prototype.isAdmin; // true

Slide 20

Slide 20 text

Prototype Pollution Attack 💥 CVE assigned due to the unsafe merge code 20 ● CVE-2019-10744 ● CVE-2024-57077 ● CVE-2020-28282 ● CVE-2025-57353 ● CVE-2022-25904 ● CVE-2026-29063 ● CVE-2023-26136 etc.

Slide 21

Slide 21 text

Just an idea: Freeze prototypes right after the polyfill if (Array.prototype.uniqueBy === undefined) { Object.defineProperty(Array.prototype, "uniqueBy", { value: function uniqueBy() {/* */}, writable: false, enumerable: false, configurable: true, }); } // Freeze prototypes Object.freeze(Object.prototype); Object.freeze(Array.prototype); 21 // ...

Slide 22

Slide 22 text

Spec bug: (Assignment) Override Mistake ● Assigning a value with the same name as a writable: false property in the prototype chain throws a TypeError ※ Object.freeze makes all properties writable: false 22

Slide 23

Slide 23 text

Spec bug: (Assignment) Override Mistake const proto = Object.create(Object.prototype, { "foo": { value: 1, writable: false, }, }); const obj = Object.create(proto); obj.foo = 2; // Throws TypeError: Cannot assign to read only property obj 23 proto Object.prototype “foo” (writable: false) null

Slide 24

Slide 24 text

Spec bug: (Assignment) Override Mistake Object.freeze(Object.prototype); const obj = {}; obj.toString = () => {}; // Throws TypeError obj Object.prototype “toString” (writable: false) 24 null

Slide 25

Slide 25 text

Spec bug: (Assignment) Override Mistake ● Fixing this spec bug is a breaking change and cannot be resolved ○ Actually breaks lodash 25

Slide 26

Slide 26 text

The Iterator.prototype.constructor Issue Added the Iterator.prototype.constructor property in the Iterator Helpers proposal 👉 Break the Web 💥 26

Slide 27

Slide 27 text

The Iterator.prototype.constructor Issue // Freeze %IteratorPrototype% (Iterator.prototype) const ArrayIteratorPrototype = Object.getPrototypeOf([].values()); const IteratorPrototype = Object.getPrototypeOf(ArrayIteratorPrototype); Object.freeze(IteratorPrototype); // make "constructor" writable: false // Use old regenerator-runtime (babel-polyfill) const RegeneratorFunctionPrototype = Object.create(IteratorPrototype); RegeneratorFunctionPrototype.constructor = RegeneratorFunction; // TypeError RegeneratorFunction Prototype 27 Iterator.prototype Object.prototype “constructor” (writable: false) null

Slide 28

Slide 28 text

The Iterator.prototype.constructor Issue 28

Slide 29

Slide 29 text

Summary So Far: Prototype Pollution ● All objects have a [[Prototype]] internal slot ● Object.freeze for prototype pollution fixes is risky due to Assignment Override Mistake ○ Fixing the Assignment Override Mistake itself is a breaking change and cannot be resolved 29

Slide 30

Slide 30 text

Stage 1 Stabilize, and other integrity traits ● Problem with Object.freeze (writable: false) ● New overridable trait ○ Prevents Assignment Override Mistake ● frozen + apply integrity traits = stabilize ○ (Other traits exist but are omitted here) 30

Slide 31

Slide 31 text

Stage 1 Stabilize, and other integrity traits 31

Slide 32

Slide 32 text

Stage 1 Stabilize, and other integrity traits ● fixed trait ○ Prevents Return Override Mistake ● non-trapping trait ○ Prevents Proxy trapping 32

Slide 33

Slide 33 text

Stabilize prototypes right after the polyfill (solved?) if (Array.prototype.uniqueBy === undefined) { Object.defineProperty(Array.prototype, "uniqueBy", { value: function uniqueBy() {/* */}, writable: false, enumerable: false, configurable: true, }); } // Stabilize prototypes Object.stabilize(Object.prototype); Object.stabilize(Array.prototype); 33 // ...

Slide 34

Slide 34 text

😓 // Stabilize prototypes Object.stabilize(Object.prototype); Object.stabilize(Array.prototype); Object.stabilize(Function.prototype); Object.stabilize(Date.prototype); Object.stabilize(RegExp.prototype); Object.stabilize(Error.prototype); Object.stabilize(String.prototype); Object.stabilize(Number.prototype); Object.stabilize(Promise.prototype); Object.stabilize(Map.prototype); Object.stabilize(Set.prototype); 34 // ...

Slide 35

Slide 35 text

Stage 1 Secure ECMAScript if (Array.prototype.uniqueBy === undefined) { Object.defineProperty(Array.prototype, "uniqueBy", { value: function uniqueBy() {/* */}, writable: false, enumerable: false, configurable: true, }); } // Stabilize prototypes lockdown(); 35

Slide 36

Slide 36 text

npm:ses import "npm:ses"; if (Array.prototype.uniqueBy === undefined) { Object.defineProperty(Array.prototype, "uniqueBy", { value: function uniqueBy() {/* */}, writable: false, enumerable: false, configurable: true, }); } // Stabilize prototypes 36 lockdown();

Slide 37

Slide 37 text

npm:ses 37

Slide 38

Slide 38 text

Summary: Prototype Pollution ● Prototype pollution is being exploited in attacks ● Workaround strategies for the Assignment Override Mistake spec bug are required before resolving this in the spec 38

Slide 39

Slide 39 text

Thenable (Promises/A+) 39

Slide 40

Slide 40 text

Thenable (Promises/A+) ● In the ES5.1 era, libraries defined their own asynchronous interfaces ● To resolve inconsistencies, the community created a spec treating the "then" method specially ● https://promisesaplus.com/ 40

Slide 41

Slide 41 text

Thenable (Promises/A+) 41

Slide 42

Slide 42 text

Thenable (Promises/A+) ● For interoperability, ECMAScript also gives special treatment to the "then" method const thenable = { then(resolve, reject) { resolve(42); }, }; await thenable; // 42 42

Slide 43

Slide 43 text

Thenable (Promises/A+) Thenable Can Break Dynamic Imports 😢 export function then(resolve, reject) { resolve(42); } // Unable to access... export function foo() {} 43

Slide 44

Slide 44 text

Thenable (Promises/A+) ● Modern JS engines use JIT compilers for optimization ● Handling side effects when accessing the "then" property adds complexity, which could lead to memory layout bugs and vulnerabilities ○ CVE-2024-43357 44

Slide 45

Slide 45 text

Side effects when accessing the “then” const thenable = { get then() { // Do something bad that corrupts the memory layout return (resolve, reject) => {/* */}; } }; 45

Slide 46

Slide 46 text

Side effects when accessing the “then” ● e.g. Comlink 46

Slide 47

Slide 47 text

Stage 2 Curtailing the power of "Thenables" ● CVE-2024-43357 was a vulnerability occurring at the boundary between DOM and JS ● Likely suppress getters or Proxy-based thenables, specifically for WebIDL ● Keep Promise.resolve/await unchanged for compatibility (expose as Promise.safeResolve?) 47

Slide 48

Slide 48 text

TC39 Meeting slide, March 2026 48

Slide 49

Slide 49 text

Summary: Thenable (Promises/A+) ● ECMAScript also supports community-driven specs regarding the "then" method ● Rejecting thenables that could cause side effects using getters or Proxies, limited to WebIDL, will mitigate vulnerabilities 49

Slide 50

Slide 50 text

TC39 Task Group 3: Security 50

Slide 51

Slide 51 text

TC39 Task Groups 51

Slide 52

Slide 52 text

Key Industries in TC39 52 ● Browser Vendors (Google, Apple, Microsoft, Mozilla) ● Cloud & Edge Runtimes / WinterTC (Node.js, Deno, Cloudflare, Amazon) ● IoT & Embedded Systems / TC53 (Moddable) ● Big Tech & Social Platforms (Meta, ByteDance) ● Enterprise & FinTech Platform (Salesforce, Bloomberg, IBM) ● E-Commerce & Digital Commerce (Shopify, Alibaba, PayPal) ● Gaming & Entertainment (Sony SIE) ● CPU & Hardware Vendors (Intel, ARM, Qualcomm, Huawei) ● Web Ecosystem Security (Socket, F5 Networks) ● Web3 & Crypto (Agoric, Consensys / MetaMask) etc...

Slide 53

Slide 53 text

TC39 Task Group 3: Security ● Specialized task group for Security & Privacy ● Evaluating security for new proposals and managing vulnerability disclosures 53

Slide 54

Slide 54 text

TC39 Task Group 3: Security “To ensure the ECMAScript security model is effective for the constantly evolving threat landscape of today and tomorrow.” 54

Slide 55

Slide 55 text

TC39 Task Group 3: Security ● Integrity and Tamperproofing ○ Stage 1 Stabilize, and other integrity traits ○ Stage 1 Secure ECMAScript ○ Stage 2 Curtailing the power of "Thenables" ● Sandboxing ○ Stage 1 Realm Globals ○ Stage 1 Compartments ○ Stage 2.7 ShadowRealms 55

Slide 56

Slide 56 text

TG3 Hot Topic: EU Cyber Resilience Act ● Covers software linked to networks and sold commercially in the EU ○ Might also include PWA / Native Web Apps ● Major incidents must be reported to ENISA SRP starting yesterday (2026/9/11) 56

Slide 57

Slide 57 text

Summary 57

Slide 58

Slide 58 text

Summary ● Addressing security issues on the Web Platform without causing breaking changes ● ECMA TG3: Security works to protect security and privacy in JavaScript 58

Slide 59

Slide 59 text

Do you have any questions? ● DOM Clobbering ● (Self-Introduction) ● Prototype Pollution ● Thenable (Promises/A+) ● TC39 Task Group 3: Security 59