13

As we know, developers are responsible for correctly escaping/validating data provided by the user before rendering or storing them. However, we must agree that it's relatively easy to forget a single input among hundreds, and finally get your web-app exploited by a XSS. Or haven't you met someone that got his/her site exploited by XSS?

Even Twitter and Orkut recently suffered from XSS. And I'm not here to blame anyone.

Suppose your browser supported whitelisting DOM elements. These whitelisted elements are the only elements allowed to execute JavaScript. You quickly realise that sounds insane because it's impossible to keep your preferred website working properly if you have such a generic whitelist. And you're correct!

But now suppose that HTML and JavaScript engines supported a syntax to allow you to whitelist elements of your own website. For example:

<head>
    <script type="text/javascript">
        document.allowJavaScript(['div.item', '#list', 'a.navigate']);
    </script>
</head>

This doesn't solve the problem of rendering <script> tags in the middle of a DOM tree, but this is much easier to solve, I suppose.

Besides my previous point, what kind of problems or flaws do you see in the described solution?

UPDATE 1: only the head element should be trusted to invoke allowJavaScript.

UPDATE 2: hopefully, the following snippet explains my idea a bit further:

<html>
<head>
    <style>
        div.outer { background-color: #aaa; width: 200px; height: 100px; }
        div.inner { background-color: #5ff; width: 100px; height: 50px; }
        ul { list-style-type: none; }
        li { background-color: #bbc; border: 1px solid #fff; float: left;
            width: 100px; height: 30px; }
    </style>
    <script type="text/javascript">
        document.allowJavaScript(['div.outer', '#list', 'a.test']);
        function register_events() {
            var list = document.querySelector('#list');
            list.onclick = function() { alert('list'); }
            var example1 = document.querySelector('#list li:nth-of-type(1)');
            example1.onclick = function() { alert('example 1'); }
        }
        window.onload = register_events;
    </script>
</head>
<body>
    <div class="outer" onclick="alert('outer');">
        <div class="inner" onmouseover="alert('inner');">
        </div>
    </div>
    <a class="navigate" href="#" onclick="alert('a');">click me</a>
    <ul id="list">
        <li>example 1</li>
        <li>example 2</li>
    </ul>
</body>
</html>

This should NOT allow execution of:

  1. alert('inner') -> we allow div.outer, but none of its child elements;
  2. alert('example 1') -> we allow #list, but none of its child elements;
  3. alert('a') -> we allow a.test, not a.navigate;
Anders
  • 64,406
  • 24
  • 178
  • 215
jweyrich
  • 187
  • 1
  • 10
  • 2
    After 9 days it is interesting to see that the highest scoring answer is NOT to do this. Is it worth rethinking your application to avoid putting this amount of trust client-side? From a core security perspective I would think so. – Rory Alsop Dec 29 '10 at 01:01
  • @jweyrich, There's actually a money solution to the XSS problem. Easy, log all code changes and make the devs sign a contract which says whoever introduces an XSS flaw by way of flawed output escaping will have 1 month of salary docked and be fired on the spot. And give all devs a bonus if no flaws have been found by the pentest-team for every x number of weeks. Also ensure pentesters try their best by giving the entire pentest team a bonus for every XSS flaw found. And prevent contact and conspiracy between the two teams by way of logistics and contracts (legality). – Pacerier Jan 26 '16 at 17:28
  • The main problem is organizations would *rather* have the XSS holes in their app if the **cost of defense** is exceedingly high. – Pacerier Jan 26 '16 at 17:29
  • You're trying to address a problem that has been previously solved, use the tried and true solution. – McMatty Mar 04 '18 at 20:28

7 Answers7

14

JavaScript is a client-side language.

In my professional opinion trusting ANY client-side implementation for added security is a waste of time, and the implementation would be a gross waste of resources.

Your time, and money would be better utilized implementing proper input cleansing and validation on the server-side environment.

Purge
  • 1,996
  • 2
  • 14
  • 26
  • 1
    That is a strange point in this general form; given that there is no alternative to trusting the client side same origin policy both for JavaScript and Cookies. – Hendrik Brummermann Jan 09 '11 at 17:03
  • 5
    The 2nd sentence seems inaccurate/not well justified. Client-side Javascript can be trusted in some cases, such as this one. Here we have a user Eunice, using her browser to visit the site X.com. Eunice is not malicious; X.com is trying to protect her and itself from attack by some other malicious user Yolanda. If X.com delivers trusted Javascript to Eunice's browser, and if that Javascript was carefully crafted to help defend against attacks by Yolanda, then the client-side Javascript *can* potentially help improve security. (See, e.g., Blueprint for an example.) – D.W. Jan 10 '11 at 07:31
11

Have you seen the work of Mozilla Security's Content Security Policy (summary)?

This is the specification.

Content Security Policy is intended to help web designers or server administrators specify how content interacts on their web sites. It helps mitigate and detect types of attacks such as XSS and data injection. CSP is not intended to be a main line of defense, but rather one of the many layers of security that can be employed to help secure a web site.

I added emphasis to the clause talking about not relying on CSP too much.

Here are some policy examples:

Sample Policy Definitions

Example 1: Site wants all content to come from its own domain:

X-Content-Security-Policy: allow 'self'

Example 2: Auction site wants to allow images from anywhere, plugin content from a list of trusted media providers (including a content distribution network), and scripts only from its server hosting sanitized JavaScript:

X-Content-Security-Policy: allow 'self'; img-src *; \ object-src media1.com media2.com *.cdn.com; \ script-src trustedscripts.example.com

Example 3: Server administrators want to deny all third-party scripts for the site, and a given project group also wants to disallow media from other sites (header provided by sysadmins and header provided by project group are both present):

X-Content-Security-Policy: allow *; script-src 'self' X-Content-Security-Policy: allow *; script-src 'self'; media-src 'self';

It seems like setting the security policy in the headers, while not impregnable to attack, makes it a lot harder than adding the security policy in-line in the DOM where it could be mutated.

I think your idea/question is a good one to think about, but as you start making in more and more comprehensive there comes a point where it is too expensive to do right.

I had a similar idea when I realized how annoying it is that all resources should be served over SSL if the page is supposed to be secured by SSL. My first thought was an idea around having checksums for each resource that is allowed to load on the SSL page (loading them in the clear would make it easier to cache them). Of course there are a host of security issues to doing that and eventually I came to the conclusion that any performance gain is quickly lost through security issues and complexity. I can see how a DOM whitelist could quickly suffer in the same way.

One final point: if you have enough knowledge to whitelist on the browser side, why can't you run the whitelist on the data you are serving before sending to the browser?

Bradley Kreider
  • 6,152
  • 2
  • 23
  • 36
  • Your link says that it's the "old and crusty" version of CSP. I think the new one is at the w3c: https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html – makerofthings7 Feb 22 '12 at 12:39
8

It seems like an interesting idea to me but since you're asking about problems with it I'll respond with some of the obvious ones. I don't see how this can be applied globally to most sites, as most would seem to need many of the dangerous javascript functions available for core functionality. E.g. onmouseover, href="javascript:alert(1)", etc. For this to be useful as an alternative to Caja or JSReg, or JSandbox it may need to applicable at any level in the DOM. For example if I just wanted to protect my <div name="untrusted"> content.

Weber
  • 1,006
  • 1
  • 6
  • 10
  • 1
    I updated the question to clarify. You should be allowed to whitelist any element, and as many as you wish. The most obvious problem is the transition to the new model, but I risk to say it's unavoidable to any in-browser solution. – jweyrich Dec 20 '10 at 19:17
  • @jweyrich, but you'd still have the same vulnerabilities for the list of elements you whitelisted. Doesnt mean you're gonna go back and fix all of THOSE. – AviD Dec 20 '10 at 19:38
  • 1
    @AviD: that's correct. One would normally whitelist only those elements that don't involve user supplied data. Otherwise, he still has to correctly escape AND validate the data. IMO it still decreases the attack surface by a very large magnitude. – jweyrich Dec 20 '10 at 19:49
  • 2
    Yes, but enough? Is it worth it? I think that as it is, there are already enough solutions out there. And while I definitely think its worthwhile finding ways to make it easier for developers to solve their own XSS issues, in most cases I find that actually *fixing* the issues take less time than *analyzing* the discovered vulns to decide which way to fix it. So, even with your solution, you'd still be doing a lot of analysis, to figure out which elements can be whitelisted, and which should be validated... – AviD Dec 20 '10 at 20:00
  • @AviD: that's a good point. I don't know if it's a tradeoff worth making. I really appreciate your input! – jweyrich Dec 20 '10 at 20:11
  • 1
    @AviD, What jweyrich is saying is that this hypothetical suggestion is something which will be implemented by the browser. The browser is the one doing all the heavy lifting, and when this solution has been fully *canonicalized*, the dev will have nothing much to do besides providing the "declaration" for the elements he want to whitelist (alike what we have in CSS and CSP). – Pacerier Jan 26 '16 at 17:27
8

Restricting the whitelist to the <head> element is not helpful because <title> is part of <head> and often contains user provided data. So the whitelist definition needs to be the first element in <head> and only the first whitelist call may be accepted.

The content of <li>example 1</li> is not trusted in the example. So an attacker could supply this:

<li>example 1><a class="test" onmouseover="alert(document.cookie)">hello</a></li>

Since a.test is on the whitelist, the code is executed. It would be possible to apply strict dom element validation. But you likely want to allow some nested elements such as <b> and <i>.

It seems a lot easier to proper escape untrusted content based on a whitelist of allowed tags and attributes.

PS: Keep in mind that there are other nastinesses such JavaScript embedded in CSS, and plugins such as flash and java.

Hendrik Brummermann
  • 27,118
  • 6
  • 79
  • 121
  • I described the 1st point in previous comments, but I agree with the rest. Very good answer! +1 – jweyrich Dec 26 '10 at 01:44
  • I put more thoughts on it, and I see this can be a bigger problem because it's NOT only about getting JS executed, it's also a possible layout change. The attacker could do something naughty like adding a link pointing to a malicious page and use CSS to overlay the entire page (like the XSS on Twitter). To deal with that, more parts of the browser would need to be touched, which is out of question. So we're back to stage zero: properly escape user data on server side. – jweyrich Dec 29 '10 at 15:00
7

XSS usually works by injecting foreign Javascript into the body of the trusted page. If you trust your code that says document.allowJavaScript(['div.outer', '#list', 'a.test']); how would you not trust code on the other page of the site saying document.allowJavaScript(['div.evil', '#securityhole', 'a.exploit']); which is produced by an XSS vulnerability? As long as both reside in the same context of the same page - which is the main idea of XSS - you have no way to know one from the other.

You could, of course, say that you have "privileged" context - say, inside <head> - and "unprivileged" context inside <body> - and this will cover part of the vulnerability, but not all of it. Since you have no way to guarantee that there won't be XSS vulnerability that would allow to inject content into the "privileged" part of the page. Of course, those would be less frequent, but it would not solve the whole problem - you would still have to have the validation on the server.

Additionally, you would introduce maintenance problem - every time the designer changes something on the page, you would have to edit the privileged elements list, while at the same time always keeping the "dynamic" parts off it. That may prove rather hard to do in real projects - especially as some pages are assembled of many independent widgets generated by separate parts of the application, that can be produced by entirely different teams.

StasM
  • 1,841
  • 2
  • 15
  • 23
  • 1
    The developer can easily guarantee that `allowJavaScript` is the 1st function to be executed, and the browser can guarantee that it's called only once. Considering that, how would you introduce a foreign javascript into the body of the trusted page and get it executed (bypassing the protection)? If the client system is compromised and injects a JavaScript in the context of a loaded page, you can't call that XSS. Traffic manipulation isn't XSS too. Please, elaborate. – jweyrich Dec 25 '10 at 07:06
  • 1
    The same way - XSS site vulnerability. Imagine site that prints title (which is in the head section) according to user input and forgets to validate... Also, it may manipulate DOM to inject stuff into head section - so you'd have to introduce DOM permissions. And what if there's some vulnerable page that doesn't have elements at all - like one generating JSON? I could supply my own and thus break the protection. – StasM Dec 25 '10 at 07:12
  • 1
    If you guarantee that `allowJavaScript` is the first JavaScript to be executed, the evil title element won't cause any harm. It's a fairly simple requirement. JSON and HTML are completely different, so you can't have HTML elements in a JSON document. If you have, it's not a JSON document, and thus you should be able to invoke the proposed API. – jweyrich Dec 25 '10 at 07:21
  • 1
    @StasM, The declaration can be way before the `` tag. Indeed, it [doesn't even need to be in ``](http://security.stackexchange.com/questions/1238/whitelisting-dom-elements-to-defeat-xss#comment200029_1248). `` is just an example. In actual implementation we could have it right after (or even before) the `<!DOCTYPE html> `. It's all up to the browser to implement. – Pacerier Jan 26 '16 at 17:27
6

Given your interest, I encourage you to read up on Ter Louw and Vernkatakrishnan's Blueprint, Dan Kaminsky's Interpolique, and Mozilla Content Security Policy. Also check out context-aware auto-escaping, as implemented in Google's ctemplate. There's a lot of fantastic prior work: make sure you don't re-invent the wheel.

Prior work on Blueprint and BEEP shows that a risk of inline security policies (as in your proposal) is that a XSS injection may allow an attacker to inject new policies. What happens if an attacker injects a new HEAD tag and then adds script inside it that invokes allowJavaScript?

Prior work also shows that stopping execution of malicious Javascript isn't enough to be secure. If an attacker can inject arbitrary stuff into your HTML page, then the attacker may be able to: (i) mount data exfiltration attacks (e.g., learn CSRF tokens or secrets on the page), (ii) deface your page, (iii) phish your users or steal authentication credentials (think of adding a login form that asks the user for their username and password, but where the form action submits to the attacker's site), (iv) mount CSRF attacks (think of an inline image or a form element that automatically loads a URL on the victim's site, or that fools the user into doing so), (v) clickjacking/strokejacking attacks (some of which don't require Javascript).

Prior work has also shown that attacks are possible, without executing any Javascript, by attacking CSS. See the paper by Huang, Weinberg, Evans, and Jackson in CCS 2010. Your scheme will likely be vulnerable to this, if there exists any injection point in the document, at least on some browsers.

Lastly, if you're whitelisting elements in the DOM, what prevents an attacker who inject stuff into the HTML page from opening and closing tags until their malicious data is in the context of the whitelisted element? For instance, if your policy says that script is allowed in the 2nd P tag under the 1st DIV under the BODY, and if there is an injection point right after the first P tag, then an attacker could inject </P><P><DIV><SCRIPT>alert('nasty')</SCRIPT>, and now the attacker's nasty script will be treated by the browser as part of whitelisted part of the page.

Overall recommendation: I would not recommend relying upon this defense. It has a number of weaknesses.

D.W.
  • 98,420
  • 30
  • 267
  • 572
1

Someone pointed me to your question as I had a very similar one but (so far) without the possibility to break through:
https://stackoverflow.com/questions/5821985/would-this-be-a-good-idea-against-xss

Maybe together we find a good solution ;)

mgutt
  • 111
  • 2