demo · v139

XSS Sidechannel

The reason for the removal: an attacker who could plant raw bytes on a page that didn't declare its charset could trigger ISO-2022-JP auto-detection and smuggle script in via escape sequences. Below: the same byte stream interpreted as UTF-8 (safe) and as ISO-2022-JP (script executes pre-v139). Toggle the declared charset and see which engine path runs.

victim page (rendered)

what just happened

raw HTML the attacker would inject (visible as text below)

<p>harmless text</p>
<!-- the next bytes look meaningless in UTF-8 but trigger ISO-2022-JP mode -->
\x1b$B (escape sequence) ... <script>alert("xss")</script> ... \x1b(B

why removal closes the door

Chrome's content-sniffing code looked at the first bytes of an undeclared HTML response. If it saw an ISO-2022-JP escape sequence (0x1B 0x24 0x42), it reinterpreted the entire stream in that encoding. Attackers used the difference between server-side string sanitisation (operating on UTF-8) and client-side rendering (in 2022-JP mode) to smuggle <script> tags through XSS filters. Use counter showed only ~0.000002% of pageloads used it; Safari already disabled it. Removing the sniff closes the sidechannel.

how to be safe (forever)

// Always declare your charset explicitly. Both work in any browser.
<!doctype html>
<meta charset="utf-8">

// Or set it server-side:
Content-Type: text/html; charset=utf-8

see also