I recently joined a company with a million+ lines-of-code PHP app. The security approach to XSS appears to be to filter output whenever the dev thought of it. Not surprisingly, they didn't catch everything, and we've got some real doozy XSS opportunities.

In a past life I've used the approach Rasmus advocates: filter all input to be XSS safe, and don't sweat it on display. This approach worked well: most data works perfectly most of the time, and code that uses filter(UNSAFE_RAW) isn't exactly forbidden but it sure gets extra attention in code reviews.

The problem I'm having is: How do you migrate from one approach to the other? Seems like now I'm going to have to encode all the contents of the database and play whack-a-mole to remove all the new double-encode bugs I'll discover. (It should be a little easier since I can grep for the existence of htmlspecialchars() but I can't grep for its absence. :) )

A really useful answer might be a blog about a successful project or even a post-mortem.

1 Answer 1


Filter on output is the preferred method of XSS defense. One reason why this method of XSS defense is popular is because it is not dependent on data in your database conforming to an arbitrary encoding set. Filter-on-input means that the view of the application must trust the database, as it is assumed this data has already been "filtered". If the database is compromised with SQL Injection, an attacker could introduce tainted data to the database. Here is an exploit I wrote that uses SQL Injection to obtain persistent-XSS on a filter-on-input application, persistent-XSS would not have been possible on a filter-on-output paradigm.

Filter-on-output is also a more secure approach to XSS defense, and I'll give you an example in PHP.

Lets say we have a PHP application that uses htmlspeicalchars($var, ENT_QUOTES); blindly, on all input. The classic case of an attacker introducing <script>alert(1)</script> would be prevented in most cases (a decode operation such as base64decode() performed after the htmlspeicalchars() would result in tainted data). However, this blanket security measure does not prevent all XSS in the application, most importantly it won't prevent XSS in DOM events. For example:

<img src="CoolPic.jpg" onclick="doSomethingCool('userInput&#000000039;);alert(document.cookie);//');" />

Or here is another example, where javascript:alert(1) is the payload:

<a href=javascript:alert(1)>go back</a>

Not all data is used in the same way, therefore its impossible to apply a blanket security measure. URLs conform to a different XSS rules than that of event handlers, and both of these are very different from the general case of XSS.

So how do you migrate from a filter-on-output to filter-on-input? DON'T. The best way to prevent XSS is using a XSS-aware tempting system such as Twig. XSS is problem with the view, so you need a view that is secure.

  • Lets treat SQL injection as a separate problem, we're using parameterized queries for that. Sep 19, 2013 at 13:52
  • Maybe I expressed this poorly: We ARE USING filter on output, and we are totally riddled with XSS opportunities. Programmers are human, and f-o-o code doesn't "look wrong" if you forget to escape sometimes. So how would you take a 1MM line project that is failing at f-o-o, and repair it? Sep 19, 2013 at 14:32
  • @Jeremy Wadhams Well filter-on-input will only make this problem much worse. Twig is a good solution.
    – rook
    Sep 19, 2013 at 14:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .