Preventing XSS Vulnerabilities in Web Frameworks

Cross Site Scripting (XSS) vulnerabilities are among the top risks to web application security. However many web application developers have only a vague idea of what XSS is, let alone how to test for it or protect against it. Many web application frameworks such as Struts or Tapestry have some controls for preventing XSS attacks. Yet I’ve seen developers disable framework protections resulting in vulnerable applications. Usually this is simply due to ignorance or lack of attention. A basic awareness of what web frameworks do to prevent XSS vulnerabilities is required to stop developers making dumb, though potentially serious mistakes.

Code injection

Cross-Site Scripting attacks are a type of injection problem, in which malicious scripts are injected into the otherwise benign and trusted web sites.

A reflected (rather than stored) XSS vulnerability exists when there exists a way for malicious users to inject code into a web page. Typically this happens when a URL parameter or some other field editable by the user is displayed to a page.

Tapestry user input

Tapestry success message

On the above page (created in Tapestry 5), the user enters some information in the text box and the text is shown in the success message. That’s fine if the user enters plain text, but what happens if the user enters HTML?

Tapestry user input containing HTML
Tapestry success message marked up


The HTML tags do not appear as text in the success message. They’re actually interpreted as HTML. We’ve just demonstrated the user injecting code into our page. Worse, what happens if the user enters Javascript?

Tapestry user input containing Javascript

Tapestry Javascript executed

The Javascript text is injected into the page and executed. Using this vulnerability a malicious user can have our trusted web page execute any script given to it.


XSS attack using code injection

The consequences of allowing any code to be injected into a web page go beyond the trivial examples shown above. This injection vulnerability opens the door to cookie stealing and ultimately to hijacking accounts and password stealing. If the injected code copies the session and cookie information to a third party (as outlined in this excellent article) then the third party can hijack the session. Depending on the content of the website, this may allow leaking of the victim’s personal information, changing of the victim’s stored data (including password and email address) and denial of service to the victim.

In practice, the vulnerability often requires a little more work to be effective. After all, the victim is unlikely to type a bunch of Javascript code into a text field just for fun. But a little social engineering or a carefully crafted URL in an email or website can cause all sorts of nastiness. The point is, we cannot control how clever and devious our malicious users are going to get with our vulnerabilities. All we can do is close them down. It’s easier to remove what may appear to be a low priority vulnerability than it is to work out how someone could exploit it.

Web Frameworks

The good news is that most web frameworks offer some protection against XSS attack. The two standard controls against XSS are:

  1. Validate all user controlled input including form fields, GET and POST parameters, headers and cookies. Only whitelisted characters should be allowed and special HTML characters should certainly not be allowed. In practice this is tricky get right.
  2. Escape all strings output to the page. Any HTML special characters will be HTML encoded and interpreted by the browser literally rather than as HTML markup.

Web frameworks usually escape output by default. For example:

Struts 2

Struts property tag (and others) will escape HTML by default. However the ‘escape’ parameter can be set to false to output the property without encoding – this is usually inadvisable.

Equivalent tags in Struts 1 (for example bean:write) have similar behaviour but with a parameter called ‘filter’.

Tapestry 5

Tapestry can output values to the page using the ${…} syntax. This is escaped and safe. Tapestry also has an output tag which is filtered by default. Setting this tag’s ‘filter’ parameter to false outputs a value without encoding – this is inadvisable. There is also an outputRaw tag which should never be used for any data that the user can manipulate.

The equivalent tag in Tapestry 4 – insert – also escapes HTML by default but this can be disabled by setting the ‘raw’ parameter to true.

Dumb mistakes

The protection offered by web frameworks is only useful if it is enabled. On several occasions I’ve seen developers explicitly disable the ‘safe’ output mechanisms provided by the framework. Often this is a copy and paste error. The developer copies a legitimate use of unfiltered output and uses it to output untrusted data. If the developer does not fully understand what the escape / filter / raw attribute is for, they may not correctly set its value to enforce escaping.

Also, when working with JSPs, it often seems easier to use scriptlets for page output. This offers no XSS protection. It is usually better to use the framework for dynamic output.

Further protection

While a full security audit for any public facing web site is a good idea, some dumb mistakes can be spotted with a somewhat simpler strategy. A simple text file search of unsafe output ( escape=”false”, filter=”false” or whatever) will highlight potential issues.

Alternatively a static code analysis tool can check for this and other common mistakes. Be sure to check what the tool is configured to scan for and if necessary create new custom rules to catch unescaped output. For example HP’s commercial code scanning tool Fortify will not catch disabled escaping in Tapestry 5 code out of the box. It does allow user created rules though so it can be configured to do this with a little work.



  • Gadha
    June 19, 2015 - 1:54 pm | Permalink

    Very nice explaination

  • Foxy
    February 2, 2016 - 11:09 am | Permalink

    Hello Stevie, thanks for your contribution. We are currently using Fortify to scan Tapestry 5 code and we could not applay any rule to the Tapestry’s .tml files, since Tapestry is not one of Fortify’s supported libraries. Did you actually tried to create custom rules to analyse .tml files? Thank you in advance.

    • February 4, 2016 - 10:44 pm | Permalink

      Hi Foxy! We did have custom Fortify rules created to analyse Tapestry code. It was just a simple regex to catch any use of filter="false". These were created by our business’s Fortify experts at our request so I’m afraid i can’t offer advice on the specifics of how this is done.

  • guido
    May 31, 2018 - 7:52 pm | Permalink

    Hi!, i use struts 1, tag , how i can solve it? html text tag not have filter attribute :/

  • Leave a Reply

    Your email address will not be published. Required fields are marked *