Skip to end of metadata
Go to start of metadata

Cross site scripting (XSS) prevention

XSS can be quite a nasty thing, so we need to ensure that we escape all the necessary input to prevent it.

Any user input or variable that a user can manipulate, when printed on a page must be escaped, so any malicious javascript is neutralized.

It's very very simple to do this

It makes a lot of sense to do it when you are developing a tool/page as is easier to know which outputs have to be escaped and which ones don't.

Here are the the general rules:

1) If the text input was entered in a text label, then you must escape the input when printing it. Here's an example:

 <c:out value="${sessionMap.title}" escapeXml="true"/>

Here' the title of the activity was just printed without being escaped. So if the teacher would have entered malicious javascript as the activity title, then the javascript gets executed.

By using the <c:out value="${sessionMap.title}" escapedXml="true"/> the input gets escaped and the javascript is printed on the page but it doesn't run.

Another way to escape a text input is by using the fn tag:

<option value="${fn:escapeXml(taskListItem.title)}" selected="true">

Some times is handy to use the fn tag as you can see up here when inserting the var output into a value.

You can use either.

2) If the input is done into a Textarea (no CKEditor)

For instance, we use textareas for students to enter their reflections after completing an activity.

So for this, we use the lams:out tag that Fiona Malikoff created.

 <lams:out value="${sessionMap.reflectInstructions}" escapeHtml="true"/>

For this, you must also include the escapeHtml attribute set to true. As that will escape any HTML entered into it.

3) CKeditor inputs

As the input is already HTML when inserted into the CKEditor, this is the input that you don't want to escape. So when printing out CKEditor inputs, you still use the c:out tag, but instead of doing escaping it, you set this as false:

<c:out value="${videoRecorderDTO.instructions}" escapeXml="false"/>

See the instructions field above. The escapeXml="false" is a must.

4) Json data

As we use some jqgrid tables what require dynamic ajax calls to get json data, there's no easy way to escape this in the front end (in some cases). For instance for the Email Notifications in monitor (see, the json data gets sent directly to the table and it just use the data as is given.

So in these cases, it's better to escape the data on the back end using Apache commons StringEscapeUtils:

import org.springframework.web.util.HtmlUtils;
 cell.put(HtmlUtils.htmlEscape(user.getFirstName()) + " " + HtmlUtils.htmlEscape(user.getLastName()) + " [" + HtmlUtils.htmlEscape(user.getLogin()) + "]");

If it is a multiline entry, you will probably also need to do a textValue.replaceAll("\n", "<br>") to convert any newlines to the correct HTML for rendering on the screen.

Do not use org.apache.commons.lang.StringEscapeUtils.escapeHtml(String) as this method does not support emoji characters.


text input form tag => <c:out value="fooBar" escapeXml="true"/> or ${fn;escapeXml(fooBar)}

textarea input => <lams:out value="fooBarTextAreaInput" excapeHtml="true"/>

CkEditor => <c:out value="ckeditorInput" escapeXml="false"/>

Json data => use HtmlUtils.htmlEscape(fooBar) in the web action.

With these 4 rules, I think we are pretty much sure that we can get tackle XSS attacks. But I think this is quite tricky and in the future we might find some more places that we haven't thought.

Testing javascript script

You can use the following script to see if you can insert it into a page

<script type="text/javascript">alert("This javascript popup should not be here");</script> 
  • No labels