I originally wrote this as a Bugtraq post, in response to the question: "XSS [Cross-site scripting] bugs are easy to discover and easy to fix, so what's the problem?" Unfortunately, it seems not to have made it through moderation, so I'll preserve my words of wisdom here for posterity:
XSS bugs are common because most web development environments make it far too easy to introduce XSS bugs.
The prevalence of any bug is directly proportional to how much extra work a programmer must do to avoid introducing it. Writing software is 90% attention to detail, but programmers are human and the more details there are to pay attention to, the more likely one will be missed.
You get buffer overruns in C code because the programmer is required to guard against them, not so much of a problem in Perl. SQL injection is more common in PHP (string concatenation and explicit quoting) than it is in Java (PreparedStatement and automatically-quoted argument insertion, or O/R mapping).
XSS bugs happen because in most web dev environments, the default is to not escape entities when writing variable data into HTML. If escaping were the default, and developers had to specifically un-escape variable data that was supposed to be HTML (and the parameter to do so were called something like "unsafe") the incidence of XSS would drop by orders of magnitude. It would also become much easier to audit unsafe usages in such apps.
Of course, the problem with systemic solutions is that they introduce the danger of systemic exploits: if a loophole is found in environment X's escaping, then temporarily every application written in X is vulnerable. On the other hand, it means that once the loophole is fixed at the source, that fix propagates out without all developers having to learn about the new boundary condition they need to guard against.