1

I've seen that this question has been asked elsewhere:

Escaping HTML strings with jQuery

The answer marked as correct by @travis says that .text() is fine. However, some people mentionned in the commentaries (e. g. @nivcaner and @lior) that this solution is not good. Where do we stand? Can I safely use it?

I'm working on a web application where an user can create a document in his browser where tables, headers and videos can be added via JQuery. When the user "saves" his document, its structure is translated into an array, where all elements of the array consist of "pure" text, with no tags (e.g. there is one array element for each table cell which contains the text of that cell, etc.). Then the array is converted into JSON format and is sent to the server and is processed / sanitized / saved in JSON format via PHP.

After that, the document can be accessed by other users, and this of course introduces potential security holes. The idea is that they read the JSON data from the server and then recreate the document via javascript/JQuery on the client side.

While I'm pretty confident that my PHP code does its job of properly sanitizing the JSON data, I'm afraid of some attack where a malicious user could trick other users into reading JSON data from an untrusted source. For this reason, I have the feeling that it's better to also validate any JSON data coming from the server before re-creating tables, etc. on the client side. Assuming that the variable currentCellText holds the (JSON read) text to be written in the table cell #mytd, can I safely use the code $("#mytd").text(currentCellText) to re-create the cell contents?

Community
  • 1
  • 1
Daniel G.
  • 47
  • 2
  • 6
  • How would someone get "tricked" into reading JSON data from your API? – tadman Jun 09 '15 at 17:25
  • @tadman: I guess it could be done using a [Man-in-the-middle attack](http://en.wikipedia.org/wiki/Man-in-the-middle_attack). Yes, it is a rather sophisticated attack and not for the average hacker. But anyway I prefer to err on the side of caution. – Daniel G. Jun 09 '15 at 17:49
  • 2
    @DanielG. use HTTPS and you can avoid that issue. – Patrick Roberts Jun 09 '15 at 17:50
  • @PatrickRoberts: that's a nice and simple solution, though a bit more expensive. But it does solve the problem! – Daniel G. Jun 09 '15 at 18:07
  • 2
    @DanielG. it depends on your hosting service. I use Heroku and it does not charge extra for HTTPS, and in fact simultaneously supports HTTP and HTTPS requests to the same server. If HTTPS costs extra for you, you might want to look into using a different host. – Patrick Roberts Jun 09 '15 at 18:09
  • Well, there's of course the cost of the SSL certificate, but it's true that it's not that expensive. I was thinking more in the sense that HTTPS is _computationally_ more expensive (and thus slower) but, after some search on the internet, it seems that with the proper configuration of the server, the overhead might be minimal. – Daniel G. Jun 09 '15 at 21:05
  • 1
    The computational cost of HTTPS is somewhere between zero and immeasurable on modern hardware. It's really not at all an issue in 2015. You'll take a bigger performance hit from serving up bloated, non-compressed assets than you will from HTTPS. – tadman Jun 09 '15 at 21:29
  • @tadman: so you think it's best to use Google's "[HTTPS everywhere](http://googlewebmastercentral.blogspot.com/2014/08/https-as-ranking-signal.html)"? – Daniel G. Jun 09 '15 at 21:57
  • 1
    @tadman : After some thought, I believe that HTTPS everywhere makes sense to avoid Man-in-the-middle attacks. If my JSON data can be compromised by such attacks, than all files served by my server can be compromised, including HTML and javascript used to "sanitize" the JSON... HTTPS is the way to go. – Daniel G. Jun 10 '15 at 11:14
  • 1
    If you need it secured, that means you need HTTPS *at the very least*. There's really no way around it. Hope that isn't too much of a hassle. – tadman Jun 10 '15 at 16:40

1 Answers1

3

From jQuery .text() article:

We need to be aware that this method escapes the string provided as necessary so that it will render correctly in HTML. To do so, it calls the DOM method .createTextNode(), does not interpret the string as HTML.

This means that the data gets stored to a text node, which by specification cannot render the stored text as HTML, so using $("#mytd").text(currentCellText) should be safe.

The 1.x line of jQuery's implementation supports this article. When you look at the uncompressed source for jQuery 1.11.3, you'll find this:

jQuery.fn.extend({
    text: function( value ) {
        return access( this, function( value ) {
            return value === undefined ?
                jQuery.text( this ) :
                this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
        }, null, value, arguments.length );
    },
    ...

The relevant line being this:

this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );

Most of the line is simply locating the correct document with which to create the text node, but the value which is the passed in string, will never directly be interpreted as HTML.

Also, looking in the uncompressed code for jQuery 2.1.4, you'll find that the implementation uses the .textContent attribute for assignment, which relies on browser implementation for a complete sanitization of the passed text, rather than partial:

jQuery.fn.extend({
    text: function( value ) {
        return access( this, function( value ) {
            return value === undefined ?
                jQuery.text( this ) :
                this.empty().each(function() {
                    if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
                        this.textContent = value;
                    }
                });
        }, null, value, arguments.length );
    },
    ...

The relevant part of this code is here:

this.empty().each(function() {
    if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
        this.textContent = value;
    }
});

As this is what executes when an argument is passed to the .text() function.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • So this basically means that .text() **will** escape quotes and double quotes, contrarily to what Lior says in the comment I mentionned above? – Daniel G. Jun 09 '15 at 17:55
  • 1
    @DanielG. No, it will not escape quotes and double quotes, but I have read that comment thoroughly, and based on the _context_ of your situation, I do not believe that escaping quotes will matter since you're parsing it as complete HTML rather than using a template to inject strings in specific parts of your HTML. – Patrick Roberts Jun 09 '15 at 17:58
  • Why will it not escape quotes and double quotes? – Daniel G. Jun 09 '15 at 18:08
  • 1
    @DanielG. Because it is not necessary to escape them to render the string as text rather than HTML. – Patrick Roberts Jun 09 '15 at 18:10
  • Makes sense! I also thought a bit about what you said, and I also think that escaping quotes will not matter for preventing code injection attacks on the context of this situation. – Daniel G. Jun 09 '15 at 21:01