The basic defense against XSS is to filter user input, but this has been repeatedly shown to be a nightmare. Just yesterday Reddit got hit by an XSS worm that created comments because of a bug in the implementation of markdown.
I believe the answer is for sites to sign the <SCRIPT> tags that they serve up. If they signed against a key that they control then injected JavaScript could be rejected by the browser because its signature would be missing or incorrect and the entire XSS problem would disappear.
For example, this site includes Google Analytics and here's the JavaScript:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ?
"https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost +
"google-analytics.com/ga.js'
type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-402747-4");
pageTracker._trackPageview();
} catch(err) {}</script>
Since I chose to include that JavaScript I could also sign it to say that I made that decision. So I could modify it to something like this:
<script type="text/javascript"
sig="068dd60b18b6130420fed77417aa628b">
var gaJsHost = (("https:" == document.location.protocol) ?
"https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost +
"google-analytics.com/ga.js'
type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript"
sig="17aa628b05b602e505b602e505b602e5">
try {
var pageTracker = _gat._getTracker("UA-402747-4");
pageTracker._trackPageview();
} catch(err) {}</script>
The browser could verify that everything between the <SCRIPT> and </SCRIPT> is correctly signed. To do that it would need access to some PK infrastructure. This could be achieved either by piggybacking on top of existing SSL for the site, or by a simple scheme similar to DKIM where a key would be looked up via a DNS query against the site serving the page.
For example, jgc.org could have a special TXT DNS entry for _scriptkey.jgc.org which would contain the key for signature verification.
To make this work correctly with externally sourced scripts it would be important to include the src attribute in the signature. Or alternatively an entirely new tag just used for signatures could be created to sign the HTML between the tags:
<sign sig="068dd60b18b6130420fed77417aa628b">
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ?
"https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost +
"google-analytics.com/ga.js'
type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-402747-4");
pageTracker._trackPageview();
} catch(err) {}</script>
</sign>
Either way this would mean that JavaScript blocks could be signed against the site serving the JavaScript completely eliminating XSS attacks.
Note that in the case of externally sourced scripts I am not proposing that their contents be signed, just that the site owner sign the decision to source a script from that URL. This means that an XSS attack isn't possible. Of course if the remotely sourced script itself is compromised there's still a problem, but it's a different problem.