Cross-Site Scripting (XSS) is a class of vulnerabilities that allow an attacker to inject malicious scripts, such as JavaScript, into a web page that gets executed on a victim’s browser. This can allow the attacker to compromise the browser and begin to attack the victim directly through the browser.
This attack is very easy to initiate for one simple reason: browsers are very trusting. Generally, they trust any content that they receive from a server; however, if the content that the browser receives has been tampered with, the browser is generally defenseless. This means the protection must fall to developers to teach the website what content is fully trusted vs. untrusted, and what to do with untrusted content.
The Commerce Cloud recommendation for protecting sites against Cross-Site Scripting is Output Encode and Input Validate using built-in mechanisms. Output Encoding is used when data that lives in the application may contain untrusted data. Once that data has left the application and is placed in a web page destined for a user, the potentially compromised data should be encoded for the context in which it lives. For example, if the context is an HTML page, the data should encode HTML markup, such as the greater-than and less-than signs (>
<
).
Imagine what you’re missing in our other guides! Stay ahead of the competition, get exclusive pro tips, and master Salesforce Commerce Cloud like never before.
👉 Subscribe NOW and never struggle with SFCC again!
Threats: Counteracts Cross-Site Scripting Attack
In Commerce Cloud storefronts, this attack is one of the most common vectors to attack storefront users. Since the attack uses the server as a staging area and directly attacks users' browsers, it is nearly impossible to detect when an attack is happening on the server, since the attack happens on client machines.
By setting up defenses inside the application, developers can ensure that attackers cannot use the eCommerce site as a staging ground to attack storefront users.
Output Encoding: The preferred method for protection against XSS, output encoding sanitizes data before being sent to browsers. This neutralizes potentially malicious text without affecting how it is processed internally.
Input Validation: Unlike Output Encoding, this method examines all parameters upon receipt. If any parameter value does not conform to an expected value, the request is rejected. Input Validation is typically harder to accomplish in non-trivial cases. Commerce Cloud recommends Input Validation to examine types of data, rather than contents—for example, verifying that the parameter item_number
is a number and not a string. Otherwise, it is generally easier to output encode as a rule.
XSS Vectors change frequently. There may be attacks that exploit specific browser functionalities or new classes of vectors that emerge. Commerce Cloud's Output Encoding mechanisms mitigate significant classes of XSS attacks in most cases.
Input Validation can further protect a site from unexpected inputs, limiting attacks. Additionally, some modern browsers implement automatic XSS protection filtering, which further restricts vulnerable inputs.
This is a typical example of a vulnerable XSS output vector. A developer may copy code without understanding the side effects. Unfortunately for the business, the code was copied from a bad example where encoding is explicitly turned off:
Welcome Back <isprint value="${user_name}" encoding="off"/>
Another bad practice involves dw.io.PrintWriter, which does not check the contents before outputting them to HTML:
<isscript>
out.println("");
out.println(custom_comment); // allow bolding
out.println("");
</isscript>
A common anti-pattern is outputting JSON data without encoding:
<isscript>
var comments = CustomerHelpers.getCommentsForProduct(product_id);
var json = JSON.stringify(comments);
</isscript>
The first example is a simple fix. The developer can change the encoding option to "on"
or remove the encoding attribute entirely, since <isprint />
encodes automatically by default:
Welcome Back <isprint value="${user_name}" encoding="on"/>
Welcome Back <isprint value="${user_name}"/>
To fix the second example, developers should be extremely cautious. The recommended fix is to encode the full value using SecureEncoder:
<isscript>
out.println("");
var clean_custom_comment = dw.util.SecureEncoder.forHTMLContent(custom_comment); // disallow all HTML
out.println(clean_custom_comment);
out.println("");
</isscript>
For the JSON output case, the correct approach is double encoding. Since this JSON contains comments to be injected into HTML, the first encoding should be for HTML. Then, the data is transferred via JavaScript, so JSON.stringify handles encoding:
<isscript>
var comments = CustomerHelpers.getCommentsForProduct(product_id);
for(var key in comments) {
if(comments.hasOwnProperty(key)) {
comments[key] = dw.util.SecureEncoder.forJSONValue(comments[key]); // Ensure JSON values are encoded
}
}
var json = JSON.stringify(comments); // turn JSON into a manageable string
</isscript>
👉 More Info on SFCC Security