1

Sometimes I like to do an Ajax request to the server, but instead of returning a json I return a html and append that html with jQuery html method. It's safe to use directly element.innerHTML in this case?

The response is served using a Django server and its template system.

Instead of this:

function loadInfo(endpoint, container) {
 $.get(endpoint)
    .done(response => $(container).html(response))

}

Do this:

function loadInfo(endpoint, container) {
 $.get(endpoint)
    .done(response => container.innerHTML = response)

}

The point of doing this is to have dynamic behaviour on some parts of the page with a little javascript as possible

grouchoboy
  • 1,016
  • 8
  • 12

3 Answers3

4

Generally neither of these is a safe practice.

Both of them take in raw HTML content and inject it into your page. This functionality is very frequently abused by malicious actors in a type of attack called XSS (cross-site scripting).

Imagine a hypothetical scenario where you have an API endpoint that returns a user's username inside a <strong> tag.

Assume you have no validation when the user is signing up, so they can set their username to whatever they want. They could, in theory, set their username to <script>alert(1);</script>. From that point forward, without extremely careful sanitization, any time you insert that username into the page, using either .html() or .innerHTML, you might insert a script tag instead.

If a user could make a simple alert display a 1, they could potentially load any script they wanted from an external web site using the same method. Using this exploit, they could trivially steal passwords, session cookies, and payment information using a purpose-built script.

The better solution would be to include your template in the client-side code, and insert plaintext data received from the server into that client-side template using .text(), .innerText, or .textContent.

IronFlare
  • 2,287
  • 2
  • 17
  • 27
  • I'm using [Django automatic html escaping](https://docs.djangoproject.com/en/2.2/ref/templates/language/#automatic-html-escaping). Can I assume that if serving classic server html is safe, using `innerHTML` with html from the same escaping engine will be safe? – grouchoboy Aug 23 '19 at 17:56
  • @grouchoboy That depends. There are many cases where Django's auto-escaping *won't* protect you. If, for instance, you include UGC in an HTML attribute, it's possible to break out of that attribute, and create additional attributes or even event listeners that could execute arbitrary code. HTML data received in a request should *always* be treated as malicious; you should never *assume* that data from an XHR is safe, as additional exploits can be found all the time. – IronFlare Aug 23 '19 at 18:14
  • 1
    I'd also recommend taking a look at [Is Django's built-in security enough?](https://security.stackexchange.com/q/27805/71979) over on Security.SE, which has more details on exploits and threat vectors that aren't covered by Django's built-ins. – IronFlare Aug 23 '19 at 18:18
  • Is there are any different if the request is XHR or not? The "normal" request response also can have malicious code. I've done an example and if I use `hello` being `src = "javascript:alert('hello');"` an alert is shown with server side rendering when I click the anchor. So I'm going to change the question. When using a template engine with autoescape is equal **unsafe** to render malicious data in server side rendering that in XHR using `innerHTML` – grouchoboy Aug 23 '19 at 18:33
  • @grouchoboy I don't quite understand the last part of your comment, but no, whether you make an XHR, Ajax, Fetch, or other request makes no difference to the vulnerability of the data that's returned. I used "XHR" to refer to all explicitly initiated, client-side web requests. – IronFlare Aug 23 '19 at 18:39
  • sorry @IronFlare, English is my third language I'm improving. Yes I know that XHR, Ajax, Fetch, axios, ... means request generated in client side with js. – grouchoboy Aug 23 '19 at 18:45
  • @grouchoboy No need to apologize! Our community is richer for having people all across the world in it! :) – IronFlare Aug 23 '19 at 18:47
1

It doesn't matter if you use jQuery html() or innerHTML. html() method uses innerHTML internally so output is the same (jQuery performs also some cross browser checks).

If you have absolute control of content on your server, and your users are not allowed to post data to server, it is safe to use innerHTML. It is your responsibility to keep your own content "clean".

What’s more Django template system autoescape (html tags / scripts) content inside template tags by default. So it is additional layer of protection.

To disable this behavior you need to mark your content as "safe" using safe template filter.

m51
  • 1,950
  • 16
  • 25
  • Some data is post by users, its name,... but to render that variables I'm using the Django template of course. – grouchoboy Aug 23 '19 at 18:00
  • For me, django default autoescape system is enough. Ofc you can use additional tools, like [bleach](https://pypi.org/project/bleach/) to be double sure, but for me - it is overkill. – m51 Aug 23 '19 at 18:03
0

In terms of 'safety' then yes, as .html() can execute scripts while .innerHTML will not. The downside of course is.... .html can execute scripts and .innerHTML cannot. .html will also do some browser compatibility checks before it ends upo calling .innerHTML anyway.

Travis Acton
  • 4,292
  • 2
  • 18
  • 30