LabKey Server and custom modules developed by our clients generate a large number of HTML pages via server-side code. All content must be correctly HTML encoded; mistakes can cause potentially severe
Cross-Site Scripting (XSS) security vulnerabilities. Incorrect encoding is an attack vector that allows hackers to run malicious JavaScript code in users' browsers, which can expose private data and cause data loss. Correct HTML encoding is absolutely critical to protecting data stored in your LabKey Server deployment.
Correct HTML Encoding
We employ multiple approaches to ensure correct HTML encoding, including developer education, using helpers/builders to generate common HTML elements, IntelliJ warnings in JSP files, using tricky values in our automated tests, instrumenting the test crawler to attempt injection attacks, manual testing, and running third-party security scanners periodically. More recently, we've instrumented the platform prohibit all potentially unsafe JSP output.
JSP Files
JSP files are used widely to generate HTML on the server and, as such, they've been the source of XSS vulnerabilities in the past. The JSP infrastructure can't determine if a
String or
Object.toString() contain correctly encoded content or not, so we've moved to prohibit direct output of
Strings and
Objects from JSPs.
The following output scenarios are forbidden in development mode:
- Direct output of a char[] via <%=myCharArray%> or out.print(myCharArray) while running in development mode will result in an IllegalStateException.
- Direct output of an Object via <%=myObject%> or out.print(myObject) while running in development mode will result in an IllegalStateException, unless that Object is a Number, Boolean, or SafeToRender (see below).
- Direct output of a String via <%=myString%> or out.print(myString) while running in development mode will result in an IllegalStateException.
Steps to Take in Your Code
You will likely need to update your JSP files so they continue to compile and run. Our recommendations:
- Encode every String with h() before outputting it. This is the easiest and most effective way to ensure that a String is safe to render.
- Use builders to generate standard HTML.
- LabKey has easy-to-use, safe builders for all the common HTML elements: LinkBuilder, ButtonBuilder, SelectBuilder/OptionBuilder, InputBuilder, etc.
- They support all popular attributes and features, and they generate well-formed HTML every time.
- Don't hand-code HTML, even for "simple" elements.
- Take SelectBuilder, as an example. It supports all the interesting top-level attributes and lets you add select options individually (provide an Option, an OptionBuilder, or a label & value), as a Collection (of Option, OptionBuilder, String, or Object), as a Map<Object, String>, or as a Stream (of Option, OptionBuilder, Object, or String). This flexibility makes for very compact, readable code.
- Use HtmlString, HtmlStringBuilder, JavaScriptFragment, or org.labkey.api.util.DOM to construct ad hoc HTML.
- Search the code base for examples.
- Stop using most JSP tags. Use the builders instead.
- LinkTag and SelectTag have already been removed.
- OptionsTag, ButtonTag, CheckboxTag, and InputTag are all likely going away soon.
- Using builders is easier, more flexible, and more readable.
- We'll likely keep a few tags around, like FormTag, CsrfTag, and ErrorsTag.
- The SafeToRender interface can be implemented to designate a class as safe-to-render, which avoids the exception at render time.
- SafeToRender means the toString() method is safe, i.e., it returns well-formed HTML or JavaScript.
- All of the builders, JSONObject, JSONArray, GUID, HtmlString, and JavaScriptFragment implement SafeToRender, so they can be output directly from a JSP.
- Enums can implement SafeToRenderEnum to allow their constants to be output directly from a JSP (but only if the enum does not override toString()).
- Your classes can implement SafeToRender as well, as long as their toString() methods return well-formed output.
You can use static analysis on the
JspWriter class to find every invocation of
JspWriter.print(String) and
JspWriter.print(char) in your code. Simply invoke
find usages on the method of interest. Finding unsafe uses of
JspWriter.print(Object) is trickier, since many invocations of this method are safe. You can use the copy of Tomcat's JspWriter class in the LabKey source to redirect the safe uses to alternative methods temporarily; see the instructions in that file.
Future changes will address non-JSP server-side rendering (e.g.,
HttpView,
DisplayColumn) and client-side rendering.
Related Topics