3

I'm thinking about using jQuery on the server-side (node.js) to validate user input (filter tags, remove every attributes except href, rel, target & style, modify css rules which are applied directly to an element and remove javascript links). Example of js link validation below:

$(userinput).find("a").each(function() {
    if (/^j/i.test($(this).attr("href").replace(/\s+/g, ""))) {
        $(this).remove();
    }
});

Now is it possbile a the attacker to write some code which let's jQuery think it's not HTML by escaping, encoding or something? Is jQuery 100% secure for this operation?

UPDATE

For code intrested users.

5 Answers 5

7

You need to verify that the HTML is valid (e. g. proper nesting of ", ', <, >). Otherwise different browsers will use different algorithms to "fix" it. This results in them seeing different things as tags.

Furthermore there is a high risk that you add too much to your whitelist. For example, the href attribute may contain active content, such as jaVascript:, vbscRipt:, java scriPt: amongst others.

On Internet Explorer there is an expression() function that can be used in CSS to execute Javascript.

I strong suggest to use one of the existing libraries to sanities user input, such as html purifier.

Please have a good look at the XSS cheat sheet.

Last but not least, keep in mind that an attack might work completely without JavaScript, by injecting a form asking for a username and password.

11
  • Isn't jQuery verifying that the HTML is valid and should I really care about vbs links?
    – noob
    Aug 12, 2012 at 14:45
  • No, jQuery supports broken HTML. There are people out there that use Microsoft Internet Explorer and according to common statistics, it is not a small number. To make things worse: According to numbers by Microsoft, in the USA 4 ‰ is still using Internet Explorer 6. Aug 12, 2012 at 14:48
  • jQuery doesnt check if userinput is XML valid. Aug 12, 2012 at 14:53
  • So if jQuery supports broken HTML I can do this: userinput = $("<div>").html(userinput).each(dostuff).html() and then it's valid right?
    – noob
    Aug 12, 2012 at 14:54
  • 1
    @micha, I doubt that. But you can try the samples from the linked cheat sheet. At least if it blows up, you will know that it was not a good approach. Aug 12, 2012 at 15:00
5

No, as it seems to indicate you are using jQuery (adept at parsing out information from HTML tags) to do input validation on the user inputting HTML that you will then display back to the user. Whitelisting safe HTML tags, and blacklisting unsafe HTML tags is the wrong method to preventing XSS.

The right method is using a lightweight markup language like Markdown or reStructuredText for user input of formatted data (like blog posts, comments, etc). This way the user can stylize their text in whatever they want, add bold text or links within their comment with a syntax like **make this bold** or [text for a link](http://www.example.com/fake/link) which it always converts into html to be displayed like <a href="http://www.example.com/fake/link">text for a link</a> in the user's browser. This let's you completely prevent say <script> inserting javascript files, or onclick onmouserover form with action attributes as your limited safe markup language is set to not allow those tags or general attributes.

Granted you have to set your language to be in a safe_mode where special characters specifically <, >, &, ", ' are HTML-escaped (into &lt;, &gt;, &amp; &quot;, and &#39; which will be seen by the user as the real character without being interpreted by the browser as part HTML source), (attributes=false) attributes aren't allowed to be inputted (e.g., onclick), as well as requiring links to start with a limited whitelist; e.g., http, //, /, ftp, mailto, news, so you don't inadvertently let users input javascript as the protocol in their links.

(This can also be accomplished by filtering the raw input through HTML purifier, if you want your users to be able to write straight-HTML like in Hendrik's answer.)

By doing this with a mature lightweight markup language, you won't forget to miss removing something regardless of how obscure it is or how your jQuery library didn't pick up on malformed HTML while sanitizing it, but one particular browser did.

3
  • I'm using jade in my website and jade has markdown support however I need to use HTML in this case. Look at my updated post if you're intrested in code.
    – noob
    Aug 12, 2012 at 17:47
  • +1, Using another language may have some benefits, if it is extremely simple. Apart from being annoying to users, it still requires tight validation. Some time ago, I came across a simple markdown parser which happily converted (link)[javascript:...] into executable javascript. Aug 12, 2012 at 18:48
  • @HendrikBrummermann, yes, the Markdown library has to be invoked properly. See How do I use Markdown securely? for details about how to do that.
    – D.W.
    Aug 12, 2012 at 23:01
3

No. It is not safe. Browsers vary widely in how they parse certain kinds of input (for especially "invalid" HTML, where "invalid" here is not especially well-defined, but sometimes even for "valid" HTML). This provides a large number of subtle ways to smuggle bad HTML into your HTML document, without jQuery realizing it.

The root of the problem is not jQuery; the root of the problem is your general approach to HTML sanitizion. I have run some experiments, tasking others to write HTML sanitizers. I've found that well over 2/3rds end up being insecure. So, my recommendation is: do not write your own HTML sanitizer. You will quite likely get it wrong.

Instead, I suggest using an existing HTML sanitizer library. I suggest using OWASP HTML sanitizer, HTML Purifier, or NeatHtml; they are well-designed and well-regarded.

7
  • Intresting, I just made a few more testings with the information I got from the other answers and jQuery seams quite safe. So can you show me one subtle way to smuggle bad HTML into my HTML document? Btw it's not a browser which is parsing this it's JavaScript V8 in all cases.
    – noob
    Aug 12, 2012 at 16:40
  • 1
    @micha - The point is that your javascript V8 that's parsing the data when its being sanitized will be different than the browser that parses the users data when its read by a user. If you parse the data the right way and let something pass because it was malformed HTML, so wasn't strictly unsafe, but the user is using a browser that does it in a wrong unsafe way (say its IE6) then that user will be vulnerable to XSS attacks on your site.
    – dr jimbob
    Aug 12, 2012 at 16:53
  • Yeah you can't rely on jquery with this, as you cant tell us without experimenting what it does, and what it will do in future. Aug 12, 2012 at 16:58
  • 1
    @micha - Your code looks good and sanitizes against the common vulnerabilities (only allow safe tags, only allow safe attributes, only allow safe protocols in links). However, the problem is that I am not confident that your server-side jQuery code will always catch and identify every html tag and attribute in the exact same way as all web-browsers (especially old ones), especially for malformed html (that browsers often allow to gracefully fail). As stuff that jQuery doesn't recognize as part of an html tag passes the validation, this could be dangerous for users of certain browsers.
    – dr jimbob
    Aug 12, 2012 at 20:54
  • 1
    @micha - However, if you ran a simple html-escaping on content not interpreted as part of an html tag (specifically convert <, >, &, ", ' into &lt;, &gt;, &amp;, &quot;, and &#39;) then this problem would be side-stepped and off the top of my head I can't think of a flaw. (Granted the problem with roll your own security is its easy to design a system you can't break, but harder to design a system that nobody can break).
    – dr jimbob
    Aug 12, 2012 at 20:59
0

There are a lot of browser-quirks which could fool your browser behaviours. Browsers have different engines, which could leads to parsing syntax-broken parts of HTML in different ways. Here's an example of kind of unexpected behaviour (I recommend you to read this book, if you want to find out more).

Moreover, you cannot be sure, what the future will bring. The new tags or attributes could be added. The most proper way is to avoid blacklisting and create the whitelist of acceptable tags.

1
  • As you can see in my code I whitelist already however the "unexpected" behaviour is not unexpected to me. BTW it can't happen because I don't output server-side vars inside the script tag.
    – noob
    Aug 15, 2012 at 18:09
-2

Here how the javascript / jquery normalising works:

var userinput = "<a href='javascript:alert(\"<iframe>\")'>\
<embed> </embed>\
<object classid='buggy plugin' hostileparams='' />\
<html></html> <script></script> < > <htmlx> </a> < > <> \
<htmz> <></a>\
<![CDATA[ > <script type=\"text/javascript\">document.write(\"X\")</script> ]]>";

var x = jQuery('<div/>').html(userinput);
var y = x;
document.write(jQuery('<div/>').text(x.html()).html());
document.write("<br/>" + y.html());

Output:

X<a href='javascript:alert("<iframe>")'><embed> \
<object classid="buggy plugin" hostileparams="">\
</object> &lt; &gt; <htmlx> </htmlx></a>\
&lt; &gt; &lt;&gt; <htmz> &lt;&gt;<!--[CDATA[ --> ]]&gt;</htmz>
  < > < > <> <> ]]>

This is weird!

2
  • 1
    Be very careful with mixing XHTML and HTML: If you validate something against XML rules, and the browser parses it as HTML, it will blow up: <![CDATA[ > <script type="text/javascript">alert("X")</script> ]]> Aug 12, 2012 at 15:07
  • Maybe because it doesnt eval in global context: supercollider.dk/2010/05/… Aug 12, 2012 at 16:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .