5

I have a text field that allows the user to type whatever he/she wants. After saving, the results are later displayed on the screen to potentially many people.

XSS seems a bit like black magic to me, so I am wondering what the current best practices are for handling this situation (from specific ways to sanitize input to specific ways to encode html to display)?

10

Escape quotes, filter out the word javascript, restrict the allowed letters, etc.

You might want to just read through the OWASP guidelines on XSS.

This additional page at OWASP should be useful to, it deals with the encoding issues in our discussions below.

  • The OWASP XSS Prevention cheat sheet and OWASP Abridged XSS prevention cheat sheet are probably worth mentioning: owasp.org/index.php/… owasp.org/index.php/Abridged_XSS_Prevention_Cheat_Sheet – Erlend Sep 1 '12 at 6:36
  • 7
    Filtering out the word Javascript doesn't increase security, all it does is restrict legitimate input (eg you wouldn't have been able to make your answer there) . – SilverlightFox Aug 24 '16 at 21:31
  • 1
    This is pretty terrible advice. Filtering has a poor history at balancing availability and integrity. Escaping just quotes is insufficient. If you can't state "the HTML output is safe-by-construction because ..." then it probably isn't, and sprinkling filters and escapers in an ad-hoc manner won't get you close enough. Secure software consistently uses tools that are safe by default. – Mike Samuel Aug 25 '18 at 13:37
10

Since you want current best practices and the latest answer here is August 2012, I thought I might as well weigh in and update this.

Best practises to prevent any type of XSS attack (persistent, reflected, DOM, whatever).

  1. Strictly validate all input. For example, if you're asking for a UK postcode ensure that only letters, numbers and the space character is allowed. Do this server-side and if validation fails, display a message to the user so that they can correct their input. Do this for all variables outside of your control, including query string, POST data, headers and cookies.
  2. Add yourself some security headers. Namely
    • X-XSS-Protection: 1; mode=block to activate reflective XSS browser protection into blocking mode instead of filtering mode. Blocking mode stops attacks like this.
    • X-Content-Type-Options: nosniff to prevent JavaScript being inserted into images and other content types.
    • Content-Security-Policy: with strict script-src and style-src's at least. Do not allow unsafe-inline or unsafe-eval. This is the daddy of headers for killing off XSS.
  3. Follow the rules in the OWASP XSS (Cross Site Scripting) Prevention Cheat Sheet when outputting values, however for rule #3 I'd do the following instead:
    • Use HTML data attributes to output anything dynamic on the page.
    • e.g. <body data-foo="@foo" />
    • Where @foo will output an HTML encoded version of the variable. e.g. " /> would give <body data-foo="&quot; &#x2F;&gt;" />
    • Grab these values out using JQuery or JavaScript: var foo = $("body").data("foo");
    • This way you don't need to worry about any double encoding, unless your JavaScript later inserts as HTML, however things are still simpler as you deal with the encoding there too instead of mixing it all together.
    • Use a function like below to HTML encode if you're using document.write, otherwise you could introduce a vulnerability. Ideally though use textContent or JQuery's text() and attr() functions.

Tackle these in reverse order. Concentrate on #3 as this is the primary mitigation for XSS, #2 tells the browser not to execute anything that slips through and #1 is a good defence-in-depth measure (if special characters can't get in, they can't get out). However, #1 is weaker because not all fields can be strictly validated and it can impair functionality (imagine Stack Exchange without the ability to allow "<script>" as an input).


function escapeHtml(str) {
    return String(str)
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;")
        .replace(/\//g, "&#x2F;")
}
4

Compose your HTML using an auto-escaping template language that, by default, escapes untrusted inputs for you.

For example, Django templates escape HTML special characters:

Clearly, user-submitted data shouldn't be trusted blindly and inserted directly into your Web pages, because a malicious user could use this kind of hole to do potentially bad things. This type of security exploit is called a Cross Site Scripting (XSS) attack.

To avoid this problem, you have two options:

  1. You can make sure to run each untrusted variable through the escape filter, which converts potentially harmful HTML characters to unharmful ones. This was the default solution in Django for its first few years, but the problem is that it puts the onus on you, the developer / template author, to ensure you're escaping everything. It's easy to forget to escape data.
  2. You can take advantage of Django's automatic HTML escaping. The remainder of this section describes how auto-escaping works. By default in Django, every template automatically escapes the output of every variable tag.

There are also a variety of contextual auto-escaped templates which are a bit smarter, and can prevent XSS even when your templates contain embedded JavaScript, CSS, and URLs.

Closure templates says:

Contextual autoescaping works by augmenting Closure Templates to properly encode each dynamic value based on the context in which it appears, thus defending against XSS vulnerabilities in values that are controlled by an attacker.

Go's template language uses contextual autoescaping:

HTML templates treat data values as plain text which should be encoded so they can be safely embedded in an HTML document. The escaping is contextual, so actions can appear within JavaScript, CSS, and URI contexts.

  • 1
    Exactly, use appropriate encoding at the use site, when you know the context instead of trying to sanitize inputs. Automatic encoding is even nicer, though you need to be aware of the details of how their context detection works, so you know in which cases it'll misdetect the context. – CodesInChaos Sep 1 '12 at 0:08
  • @CodesInChaos, I've spent the last year looking for context detection schemes that match template authors' expectations. Code injection isn't going to disappear overnight, but the next generation of tools will be much more robust. – Mike Samuel Sep 1 '12 at 2:50
2

The single best practice: strictly control type and sanitize your data.

Always use proper method to output data according to type: never render what was entered as text in HTML (i.e. assign user-entered text data to text node's .data property in DOM, not to magical .innerHTML). Never use input in eval-like construction or as substring of database query - proper tools would be expression parser and placeholders respectively.

Reject or clean-up everything coming to your program from external sources (including your very own internal databases, that could be compromised too!) to strict set of allowed patterns.

Use established and proven protocols and serializers to transport data, preferably one that have native implementation on both sending and receiving end (JSON serializer, protobuf, Storable in Perl, etc.) instead of homemade quoting.

0

I have a text field that allows the user to type whatever he/she wants. After saving, the results are later displayed on the screen to potentially many people.

That's the problem. Do NOT trust user's input... To avoid XSS you must sanitize the user's input before storing it in the DB.

Some things you should check:

  • Do not allow HTML tags. Once you have all the tags forbidden, then you could allow some of them (like this site) like <hr> <b> etc. Or creating your own tag system, [img] [b]...

  • Do not allow " or ', replace them by their HTML code &quot; and &#39;

  • If you are planning to allow hyperlinks, be sure that the url are valid. In other words, they must start with http:// or https://

Probably, the language on which you are developping the application has functions to check most of the previous points (at least, checking quotes and HTML tags).

  • 4
    I disagree. I see no reason to sanitize the input from a text field if there are no specific constraints (e.g. should be an email). The clean solution is to encode the output. And the encoding should happen when outputting, and not when storing. – CodesInChaos Aug 31 '12 at 12:43
  • 1
    Perhaps I am not interpreting your response correctly, but if you don't filter on the way in, you run the risk of an (SQL, XML, etc.) injection attack. I am not sure why you would ever have a input from a user that has no constraints. If you have a form asking for something, its usually something specific - it may be broad (e.g., tell me your life story), but it should not include scripts, injection, etc. Your comments also suggests its okay to echo the encoded attempted attack back to the screen, which would at the least look bad and confuse other visitors. – Eric G Aug 31 '12 at 14:35
  • @EricG You're typing this on a programmers website. We discuss programs, and parts of programs. Which include SQL, XML, HTML, JS, whatever the "user" likes. And it's just as common for these discussions to take place on general purpose blogs, forums, email etc. As to how you decide whether something includes "injection" - ! – sourcejedi Aug 31 '12 at 21:49
  • sourcejedi, I am not sure I understand your comment, CodeChaos was implying you do not need to filter on the way to storage. That is the exact reason a SQL injection attack will occur, if you don't filter input into storage. I belive this is actually a security site.. nonetheless, programmers need to abide by business requirements and if the business says i want a form for my end-users to submit "their life story" it should include "their life story" and not htmlencoded evidence that someone tried to do SQL injection or XSS, that would be poor presentation. – Eric G Aug 31 '12 at 22:47
  • 3
    What the user enters is only text. It only becomes something different when you treat it differently. For example when you insert it into html, you must html encode. When you send it to a raw sql query(I'd avoid that in the first place) then sql-escape it. But you only know what encodings when you know the context in which it will be used. For example in a forum software I wouldn't apply any sanitization to user posts, the only thing I'd do is a html encode directly before outputting it. And even then, I'd prefer a template language that takes care of that automatically. – CodesInChaos Sep 1 '12 at 0:06
0

If the server only serves entities and values (so doesn't serve DOM parts), and doesn't build html, encoding values creates encoding issues because the part which integrates these values into the DOM is already supposed not to trust its inputs, which results in double encoding problems.

I think the role of such a server (which doesn't build DOM but only stores and serve data) is not to sanitize inputs. However, it can still validate inputs early and return a 400 Bad Request if an attack pattern is detected. This way, the server contributes to protection against XSS without all problems of data modification due to sanitizing.

I think sanitizing server-side must be reserved to the servers which build DOM server-side: which serve HTML pages or HTML page parts.

But if DOM is modified client-side, the JS code must not trust its input and prevent from XSS injections.

Problem is: in Java, OWASP security libraries offer APIs to sanitize HTML but not to only valisate it. To do so, I've found the hibernate annotation @SafeHtml which uses JSoup and can be used easily on DTO fields with @Valid annotation to enable validation on a class/method/parameter.

0

Excuse me if i'm not very clear. English is not my mother tongue. The best way to ensure a great level of security in your product is to follow the golden rule: "Never trust user inputs"

So you must always assume that a user will try to send unexpected data to your app, that he will not follow protocols (ie: he will spoof some fields, he will replay request, etc.) And you need to predict his behavior.

For the XSS point you should

-> Validate the user input (ie: if you expect to get a name, you should block all values that contain special char such % or ", if you expect a phone number you should block values that contains letters, an so on)

-> Sanitize the user input (ie: escape all character that have a specifical semantic for the interpreter in the backend. For the XSS it's ussually html/javascript so you should care about ", <, > in priority)

Like have said our fellows there is a great guide made by the OWASP concerning the XSS prevention: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.