HowTo:Protect Your Users by Ensuring that Data Never Executes as Code
A Cross-Site Scripting (XSS) attack is one in which a script is entered into the data stream. Then, when the data is displayed, the script executes. This guide explains how an XSS attack works, and shows you how to guard against it.
In general, the idea is to encode incoming data, so that it is displayed when accessed, rather than executing. That encoding is done automatically when data is entered into a Form that is running in the platform. But when you create an external Web Form, you need to do it yourself.
Executing a Script Stored as "Data"
To see how XSS works, you'll create an object with a data field and put a tiny script into that field. Then, when you display the data, you'll see that the script executes in the browser.
Here's the process:
- Create a TestObject with one textfield, TestData.
- Create a record in that object.
- In the field, store the following data: <script>alert("You have been hacked!");</script>
That code is a script that opens a dialog and displays a simple message--but it could conceivably do much more, like opening a cookie on the user's system and sending its information elsewhere. - Now create a JSP Page, TestPage.jsp to display the data in that field. For example:
<html> <%@ taglib uri="/c" prefix="c" %> <head> <title>Test Page</title> </head> <body> Here is the data from the first and only record: <% // Get all records that have testfield data. Result results = Functions.searchRecords("TestObject", "testfield", "testfield != BLANK"); String data = null; // Get the data from the first record. ParametersIterator iterator = results.getIterator(); while(iterator.hasNext()) { Parameters recordParams = iterator.next(); data = recordParams.get("testfield"); break; } %> <!-- Insert the data into the HTML stream --> <%=data%>. </body> </html>
- Next, visit that page, at https://{yourDomain}/networking/pages/TestPage.jsp
When you go there, you encounter the dialog box generated by the script that was stored as "data".
Although it is possible to enter the script using the platform interface, as we have done here, the most likely scenario for XSS is in the form of data that originates from an external source, submitted to the system in a Web Form. For example, the script could be part of a comment on a blog post. Anyone who displays the comments then unknowingly executes the script.
Such problems are prevented by encoding user data, before it is displayed. You can do that using either Java APIs or JavaScript APIs, discussed in the sections that follow.
Warning: It is never a good idea to "double encode" data. You can store data in encoded form, or display it that way. You never want to do both. (Since encoding the data takes additional space, it is typically desirable to do the encoding when the data is displayed.) Similarly, make sure that the data runs through only one encoding process before it is displayed.
Using Java APIs to Encode Displayed Data
Protecting users in such scenarios is a matter of ensuring that such "data" is always displayed, rather than executed. To do that, you encode the data, so the browser sees <script>, for example, rather than <script>. (When it sees <script> it goes into execution mode. But it when it sees <script>, it substitutes the left- and right-angle brackets, and then simply displays the resulting string.
The methods you need to encode displayed data are available in the com.platform.api.XSSEncoder class:
- encodeForHTML - Encode data for display in an HTML page
- encodeForHTMLAttribute - Encode data for display in an HTMLAttribute
- encodeForJavaScript - Encode data before inserting it into JavaScript code
Here's the JSP page, rewritten to encode the data. When you run it now, you no longer see the dialog box. Instead, you see the code that would have generated it, had it been allowed to run.
<html> <%@ taglib uri="/c" prefix="c" %> <head> <title>Test Page</title> </head> <body> Here is the data from the first and only record: <% // Get all records that have testfield data. Result results = Functions.searchRecords("TestObject", "testfield", "testfield != BLANK"); String data = null; // Get the data from the first record. ParametersIterator iterator = results.getIterator(); while(iterator.hasNext()) { Parameters recordParams = iterator.next(); data = recordParams.get("testfield"); // **** ADD THIS LINE **** data = com.platform.api.XSSEncoder.encodeForHTML(data); // *********************** break; } %> <!-- Insert the data into the HTML stream --> <%=data%>. </body> </html>
Using JavaScript APIs to Encode Displayed Data
In JavaScript, use the jQuery Encoder APIs:
- encodeForHTML - Encode data for display in an HTML page
- encodeForHTMLAttribute - Encode data for display in an HTMLAttribute
- encodeForJavascript - Encode data before inserting it into JavaScript code
- encodeForURL - Encode data for use in a URL
- Considerations
-
- When the JSP/Html Page is set to include headers, the jQuery Encoder APIs are automatically included in the page.
- When the JSP/Html page does not include platform headers, load the jQuery-encoder library as a Static Resource, and use it in your page