Since the days of phone phreaking, we've known in-band signalling is just a terrible idea. Any time a legal message token/signal/sequence in your data payload can escape data to metadata/code status, encapsulation complexity goes way up and at least one implementer is going to screw something up in a bad way. XSS was entirely predictable from the design of <script> tags and onload, onclick, etc. handler HTML tag attributes.
One alternative would be to force all <script> tags to come immediately after the <html> tag (before any <head>, <body>, <style>, etc. tag), and have it be a fatal error disabling scripting if a script tag came after any other html tag except for <html>. You'd also need to reverse the way onlead, onclick, etc. handlers are set, having them set from the script side instead of setting handlers via html tag attributes. If you were extremely careful about the design, you might be able to have scripts opt-in individual functions to being handlers specified in HTML tag attributes, but the corner cases get hairy very quickly. If you need nice general libraries handling clicks, etc, it's probably best to have your scripts scan for the tags to have handlers attached rather than having the tags decide their own handlers.
The are some other solutions involving unambiguous length-prefixed type-tagged sections clearly separating text and code, but those are better suited to non-textual formats, and a weird hybrid text-and-binary format (such as PDF) is another type of security minefield.
Edit: formatting. Also, one of my favorite classes was a retrospective look at a variety of computer systems, critiquing very good and very bad design decisions. We looked at X11, zlib, Therac-25, Ariane 5, etc.
'unsafe-inline` still allows external scripts, so isn't really effective on it's own, but yeah CSP does generally solve the problem the gp is talking about.
In general I do agree that it would be nicer if script (and style) tags weren't in-band; the Link header solves this for style, and I think could do similar if it were specified for scripts, it's just a pity Chrome never wanted to implement it.
However, this just moves the problem around. While it could be argued specifying JS out-of-band would make it less likely for XSS to occur, anywhere that JS is specified is still likely to be programmed dynamically, with external inputs (db values, etc.), so the risk remains.
For example, a lot of modern XSS leverages innerHTML rather than server-side markup insertion. e.g.:
element.innerHTML = fetchData(endpoint);
This code is XSS-able (especially in the case of things like jsonp payloads), and it's completely agnostic of where the JS is specified (in-band or out-of-band).
The proper solution, as a sibling commenter mentions, has already been specified: it's CSP and it works well if used.
Presumably element here is <head> or later, so scripts wouldn't execute. Also, you'd presumably define things such that new <script> tags would be ignored if they were parsed temporally after the first non-<stript>, non-<html> tag, regardless of their position in the DOM, so that you'd need to explicitly eval any dynamically loaded scripts.
Thanks for the pointers to CSP. I'll have a look. In any case, the default should have been to ovoid in-band signalling, and perhaps have a CSP-like mechanism to relax security constraints. Sure, hindsight is 20/20, but precedents in phreaking are obvious.
One alternative would be to force all <script> tags to come immediately after the <html> tag (before any <head>, <body>, <style>, etc. tag), and have it be a fatal error disabling scripting if a script tag came after any other html tag except for <html>. You'd also need to reverse the way onlead, onclick, etc. handlers are set, having them set from the script side instead of setting handlers via html tag attributes. If you were extremely careful about the design, you might be able to have scripts opt-in individual functions to being handlers specified in HTML tag attributes, but the corner cases get hairy very quickly. If you need nice general libraries handling clicks, etc, it's probably best to have your scripts scan for the tags to have handlers attached rather than having the tags decide their own handlers.
The are some other solutions involving unambiguous length-prefixed type-tagged sections clearly separating text and code, but those are better suited to non-textual formats, and a weird hybrid text-and-binary format (such as PDF) is another type of security minefield.
Edit: formatting. Also, one of my favorite classes was a retrospective look at a variety of computer systems, critiquing very good and very bad design decisions. We looked at X11, zlib, Therac-25, Ariane 5, etc.