Still not good enough if you have code that emits HTML using print statements. It would have to parse the output on its way to the browser. That's just another reason why the idea of automatically "fixing" the content borders on insanity.
The only way I can see to handle all this securely and automatically is to actually parse your templates and keep track of each context...
There is a reason I specified templates there. Templates act as a way to separate your output into stuff that is safe (stuff you wrote) which doesn't need escaping, and stuff that's come from somewhere else that does need escaping. The templates are effectively doing the same job as binding values in an SQL query; separating the stream into things that should be escaped and things that should not be escaped.
No amount of escaping, automated or otherwise, is going to be able to save you in the situation you proposed, because you have definitely safe content mixed in with potentially unsafe content. Deciding what parts of the stream should/should not be escaped is effectively a halting problem. However, if what parts need escaping is specified via something like templates (where it's actually harder to do what you're suggesting than use them as intended) then you change the problem into "How should I escape these bits"? The use of templates effectively makes this separation into an opt-out process rather than an opt-in one.
The argument I was trying to make with my straw man example above is that the question of how to actually escape them correctly, in an opt-out kind of way, while possible, is needlessly complicated and cannot be done elegantly or without considerable performance degradation. And this is just a natural result of how we have lots of languages with different escaping rules that can all nest within each other.
Much better to spew errors if you pass the data to the client without running any of those quoting methods or explicitly mark it as safe in some other way. That way, the programmer is forced to decide how it should be handled on a case-by-case basis, and code that already does the right thing requires minimal changes (calling one function to mark tainted strings as clean in spots where you really do mean to output the content without quoting).
I think doing this in your framework is every bit as ugly as the insanity I suggested and actually supports my argument. I can only think of two ways to do this in popular server-side languages:
- A lot of very ugly and slow reflection (and TBH, I'm not entirely sure about this one)
- Implementing your own types and forbidding use of your language's standard library and built in types (how would your framework copy information regarding tainted-ness of a value from one to another if you created it using a standard library substring function that only understand the standard string objects/scalars?).
It could be done pretty cleanly as a static analysis tool. It's just that if it was a static analysis tool it wouldn't actually fix the problem as it wouldn't be able to catch all cases (because halting problem). It would certainly be a very useful heuristic and lower the overall number of XSS vulnerabilities in peoples applications, but it wouldn't actually make them impossible in the same way as binding values to a query makes SQL Injection impossible.
TLDR: We are doomed to have to be eternally vigilant about XSS. Frameworks that have opt-out XSS protection only handle the common case which means we still have to worry about the uncommon cases because correctly handling those is too painful.