Preventing XSS in Your Application

Explain XSS and How to Mitigate It?
This is one of my interview questions; almost everyone can explain XSS decently but very few understand how to protect against it. I have heard everything from sanitizing input (wrong) to using ESAPI to filter incoming requests (even more wrong since ESAPI in support mode with no new features and is mostly used for black list filtering), to a few mentioning output encoding (mostly right but context matters) and so far, only one has mentioned Content Security Policy headers.

What is XSS
Cross Site Scripting happens when a malicious actor sends a string to a server. This string is delivered to the victim’s web browser and the browser interprets the string as a script to execute. These scripts can perform many different malicious actions on behalf of the malicious actor using the victim’s authenticated sessions.
There are three types of XSS. 1) Reflected XSS is when input sent to a server in a request and is immediately returned; an example would be a search parameter whose value is returned in a page. 2) Stored XSS is when the server retains the string and later delivers it when certain pages are viewed such as a posted message in a board or a Name field. 3) The third type, DOM based XSS, is not addressed.


The above image shows a XSS embedded in a digital certificate. I was able to create a digital certificate with a XSS in the certificate’s name. This allowed the XSS to bypass the system’s Black and White lists. When the admin viewed the certificate, the server decoded the cert and sent the strings to the admin’s web browser where the XSS was rendered. In this case, the XSS was a Rick Roll (one of my favorite demonstrations) but any javascript could have been embedded in the field. The above was fixed years ago before it ever reached production.

How to Protect Against XSS
There are two, effective methods to defend against XSS and both must be done:
1) Encoding based on the Context of the Output – This is your first line defense against XSS, making sure that any untrusted output is encoded/escaped properly based on the context in which the data is being placed. As an added bonus, this also defends against HTML injection. There are slightly different encoding rules for various contexts such as HTML data, Attributes fields, URLs, etc… The OWASP site has a decent listing of all the various contexts, the required encodings and suggestions of libraries to help with the encoding: https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
It is strongly recommended to utilize a framework that properly and automatically handles output encoding such as Angular and others.

2) CSP or Content Security Policy is a set of html header directives sent to the web browser that helps it enforce what scripts should be included and are acceptable. CSP needs to be enacted along with output encoding; it will help make sure any missed output encodings will be less dangerous. While CSP will stop unwanted scripts, it will not stop HTML injection which can be used to rewrite a page for malicious purposes. CSP will not protect against XSS injection into an existing script; if user input is utilized within a CSP allowed javascript, it must be escaped and handled very carefully. More information and usage of Content Security Policy (CSP) header directives: https://en.wikipedia.org/wiki/Content_Security_Policy

A couple of simple, secondary protections to add:
1) In addition to Output Encoding and CSP, the HttpOnly flag will tell browsers not to allow java script to read the session information embedded in a cookie. This helps protect against XSS from gathering session information. https://www.owasp.org/index.php/HttpOnly

2) X-XSS-Protection header also helps to prevent some XSS in a few browsers but can be bypassed. https://www.owasp.org/index.php/Security_Headers

What Not to Do
There are a few techniques I constantly see being recommended that should be avoided.
1) Black Lists – Input Filtering of certain characters in an attempt to prevent XSS never works. Do not attempt to do it even as a secondary measure. XSS black list evasion techniques are very advanced and it is impossible to filter all such attacks. Web Application Firewalls (WAFs) are professional products using black lists and they fail to do this properly. If you wish to learn more about bypassing xss black lists/filters, I suggest you read @BruteLogic‘s blog. https://brutelogic.com.br/blog/the-easiest-way-to-bypass-xss-mitigations/

2) White List – While OWASP and others say it’s an effective, secondary mitigation, it adds almost nothing in terms of security, can be bypassed on many systems, and can mask the true problems.

A) Input coming from complex sources is very difficult in white list. The above XSS image with the XSS in the digital certificate is a perfect example of where input filtering fails to stop XSS. The digital certificate was encoded and the server’s black and white lists did not perform the decoding to validate all internal fields. Sometimes fields must accept an extensive form of data coming into a system that would allow XSS. In other cases, malicious input may come into a system from places other than the web front end, such as log files, file names, machine names, etc. Input filtering on the web front end would not stop these attacks. While input filter is critical to stop many types of attacks, it is just not an effective mitigation for XSS.

B) Proper output encoding based on the context and a strong Content Security Policy (CSP) is going to cost your developers time; they both must be done and will fully mitigate any XSS issues. For most teams I have worked with, developer resources are stretched thin; asking them to perform work that is not needed is wasteful. Do not waste your developers time with unneeded mitigations.

There are some cases where white listing can be effective, such as when some selective markup is allowed, such as a message board, but these cases are rare and extra caution needs to be taken to make sure it is done safely.

3) Encoding data on Input – This fails because different encodings are needed based on where the data is being placed. At the time of development, it is impossible to know all possible later usages of the input. Likewise, in many systems, data comes into a system from sources other than the web front end (such as log files).

Note on WAFs
Relying on Web Application Firewalls (WAFs) – While I strongly support using a WAF, it is a very good Black List but can (not easily) be bypassed; it is a secondary measure to protect against XSS (although it does much more than just XSS). Do not rely on a WAF; use output encoding and CSP. Also, not all WAFs are created equal.

How to Test for XSS in Your Application
Many automated tools do a great job at providing broad coverage for XSS but they are not nearly as skilled at bypassing filters as a human. A half decent Black List can stop any automated scanner, such as Burp or AppScan, looking for XSS but will not protect your system from true attacks. Since the first line defense against XSS is proper encoding based on the context of the output, the testing needs to find any output that isn’t being properly sanitized. In a non-production environment, disable CSP, WAFs and any input filters; by disabling these secondary protections, testing tools will have an easier time finding fields were the output isn’t being properly encoded. Do not forget to re-enable the secondary defenses before pushing out to production. Note: Do not test in production, Stored/Persistent XSS can be destructive and cause problems for those viewing your site and will give strong hints to attackers there is a potential weakness (amongst other issues).

Recap
Mitigate XSS with proper output encoding/sanitization based on the context where the data is being places; the best way to do this is to utilize a framework that automatically handles the output encoding for you. Utilize Content Security Policy as a strong, secondary measure. When testing for proper output encoding, disable all input filters such as white lists black lists, WAFs and remove CSP.