Then XSS attacks would insert </without-scripts> before <script></script> =)
What we need is literal separation of control statements (eg, <script>) from content such that neither can be easily misinterpreted, but that would be a significant departure from existing design.
A modernized, preferably static version of perl's taint mode might work. You need two types of strings, one for trusted and one for untrusted strings, all your output functions accept only trusted strings, all your input or request parsng function return untrusted strings, and all naive string manipulation functions return untrusted strings if at least one of their arguments are untrusted. Then the possibly vulnerabe code is limited to a few statically identifiable routines that take untrusted strings and return trusted ones. They still may be buggy, but at least you know which parts of your code may induce vulnerabilities and need special attention.
And with modern type systems, these types might even be phantom types incurring no runtime overhead. Athough things like the differences between "safe for passing to a browser" and "safe for passing to my SQL-server" might compliate the architecture.
Ruby has a taint mode for String too. I think it's used in Rails, or maybe they rolled their own, but the concept is definitely there.
Problem is, at some point you have to be able to display user-entered data. You indeed mark it as tainted, or equivalent, then escape it as best you can. The issue here was a bug in the escaper. Tainting was working as planned.
That would require escaping the HTML on the server prior to output, which is exactly what we already have (and regularly fails, even when people think they're doing it right).
The real analogue to SQL query parameter binding would be an output format that keeps the two distinct.
What we need is literal separation of control statements (eg, <script>) from content such that neither can be easily misinterpreted, but that would be a significant departure from existing design.