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 victims 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, input validate, using our built-in mechanisms. Output Encoding is used when data that lives in the application may contain untrusted data, but 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 – e.g. if the context is an HTML page, the data should encode HTML markup, such as the greater-than and less-than signs (><)
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, customer developers can ensure that attackers cannot use the ecommerce site as a staging ground to attack storefront users.
XSS Vectors change frequently. There may be attacks that attack specific functionality of a browser or a new class of vectors may be discovered. The defenses that Commerce Cloud provides customers for Output Encoding solves significant classes of XSS attacks in most cases. Input Validation can further protect a site from unexpected inputs, limiting attacks. An additional, free, defense is that some browsers implement automatic XSS protection filtering, which further restricts vulnerable inputs.
This is a typical example of a vulnerable XSS output vector. Here a new developer has decided to 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
Similarly, there is an API, dw.io.PrintWriter, exposed as the variable “out,” that does no checking against the contents before simply outputting to the generated HTML. The developer is attempting to output the contents of a variable, custom_comment. The customer comment should be able to bold certain parts of the review.
out.println("");
out.println(custom_comment); // allow bolding
out.println("");
Finally, a common anti-pattern is to try to output JSON data without encoding. While this looks like a workaround to incorrect encoding, the issue here comes from having a JSON context hide an HTML context:
var comments = CustomerHelpers.getCommentsForProduct(product_id);
var json = JSON.stringify(comments);
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
...
Welcome Back
To fix the second example, developers should be extremely cautious. The recommended fix is to reject this feature and as it would be rife with potential issues and just encode the full value.
It may be tempting to Input Validate, however validating HTML code is very difficult and not recommended to do by hand. All other attempts at encoding are hacks and not production-ready.
out.println("");
var clean_custom_comment = dw.util.SecureEncoder.forHTMLContent(custom_comment); // disallow all HTML
out.println(clean_custom_comment);
out.println("");
The final example is a prime example of Double Encoding. In Double Encoding, a piece of data may have to be wrapped in multiple layers of encoding in order to remain safe. This makes the values difficult to handle, but should be treated as a simple stack. First encoding used is the last decoding used.
Since this JSON contains comments to be injected into HTML, the first encoding should be for HTML. The data is then transferred via JS, so JSON stringify encodes the data handily. injectComments would then have to decode the JSON, and insert into HTML, still encoded. This might look something like this:
var comments = CustomerHelpers.getCommentsForProduct(product_id);
for(var key in comments) {
if(comment.hasOwnProperty(key)) {
comments[key] = dw.util.SecureEncoder.forJSONValue(comments[key]); // Ensure JSON values are encode
}
}
var json = JSON.stringify(comments); // turn JSON into manageable string
Unlock a FREE PDF with SFCC B2C Certification questions from our Udemy Course. Join our newsletter!
Check your email, you’ll receive a copy in a few seconds. If you don’t see it, please check your spam folder.
Do you like cookies? 🍪 We use cookies to ensure you get the best experience on our website. Learn more.