#17419: Add a JSON template filter
-----------------------------------+-------------------------------------
     Reporter:  lau                |                    Owner:  aaugustin
         Type:  New feature        |                   Status:  new
    Component:  Template system    |                  Version:  master
     Severity:  Normal             |               Resolution:
     Keywords:  json template tag  |             Triage Stage:  Accepted
    Has patch:  0                  |      Needs documentation:  0
  Needs tests:  0                  |  Patch needs improvement:  0
Easy pickings:  0                  |                    UI/UX:  0
-----------------------------------+-------------------------------------
Changes (by amacneil):

 * cc: adrian.macneil@… (added)


Comment:

 I think it's very important to add some form of `json` filter. Right now
 there is a situation where developers are putting the output of
 `json.dumps()` directly into templates and marking it as safe, which
 causes an instant XSS vulnerability (I have seen this in the wild, hence
 why I came across this ticket). This is a fairly complicated security
 issue, so Django should make it easy for users to do the right thing.

 To summarize previous comments, there are two different contexts where you
 may wish to use JSON in templates:

 1. Inside HTML (e.g. `data-*` attributes)
 2. Inside CDATA (e.g. `var foo = {};` inside a `<script>` tag)

 The problem here is that each context requires different escaping rules,
 and there is no easy way for Django to tell which context you are in.

 To escape for context 1 (inside html attributes) is actually fairly easy -
 run `json.dumps()` on the object, then apply regular html escaping:

 {{{
 foo = {'foo': 'bar'}
 json_str = json.dumps(foo)
 }}}

 {{{
 <div data-foo="{{ json_str }}">
 }}}

 {{{
 <div data-foo="{&quot;foo&quot;:&quot;bar&quot;}">
 }}}

 To escape for context 2 (inside script tags), the problem is that HTML
 escaping will produce invalid javascript:

 {{{
 <script>
 var foo = {{ json_str }};
 </script>
 }}}

 {{{
 <script>
 var foo = {&quot;foo&quot;:&quot;bar&quot;}; // causes JS parse error
 </script>
 }}}

 Currently people are solving this by marking the JSON string as safe,
 incorrectly assuming that the output is safe inside a script tag. However,
 as has been mentioned above, some valid JSON has special meaning inside
 script tags:

 {{{
 foo = {'foo': 'bar </script> attack'}
 json_str = json.dumps(foo)
 }}}
 {{{
 <script>
 var foo = {{ json_str|safe }};
 </script>
 }}}
 {{{
 <script>
 var foo = {"foo":"bar </script> attack"};
 </script>
 }}}

 The problem here is that HTML has no knowledge of parsing JS, so treats
 the first `</script>` as the end of the script tag, and allows the
 attacker to insert arbitrary HTML or JS afterwards.

 This can be solved simply and effectively by replacing unsafe HTML
 characters with unicode escape sequences. The characters which should be
 replaced are `&`, `<`, `>`, `\u2028`, and `\u2029` (the last two are
 treated as newlines by some javascript engines, which may allow an
 attacker to begin a new javascript instruction - I haven't seen them
 mentioned in this thread yet). These 5 characters are also replaced by the
 [http://api.rubyonrails.org/classes/ERB/Util.html#method-c-json_escape
 json_escape] filter in Rails, which has a similar purpose. Safe output
 will look like this:

 {{{
 <script>
 var foo = {"foo":"bar \u003c/script\u003e attack"};
 </script>
 }}}

 Replacing these characters is easy to do, and valid in both contexts (html
 and script tags), so they should always be replaced by any Django `json`
 filter. I have created a simple implementation of a json filter which
 replaces these characters here:

 https://gist.github.com/amacneil/5af7cd0e934f5465b695

 The last remaining issue is whether the output of this filter should be
 marked as safe. The alternatives are:

 * Automatically mark `json` filter output as safe. It will work as
 expected inside script tags, but any use inside html attributes will
 result in invalid HTML and potential XSS attacks.
 * Do not mark output as safe. JSON will be correctly escaped inside html
 attributes, and use inside script tags will result in malformed JS. Safe
 use inside script tags can be achieved by using `{{ data|json|safe }}`.
 Developers must be made aware that the `safe` filter should ONLY be used
 when in the context of a script tag, and not for general HTML.

 Neither of these is a great option, because it relies on the developer
 knowing/remembering that output is safe in one context, but unsafe in
 another. However, of the two I would probably prefer the second option
 (JSON output is unsafe by default, and must be manually marked as safe for
 use inside script tags), only because it is more safe by default, and
 requires explicit thought to force the output as safe, which should ring
 alarm bells and cause extra caution.

 A final alternative may be to create another filter, named something like
 `scriptjson`, which has the same effect as `json`, but which marks the
 output as safe. Documentation could state that this filter should ONLY be
 used inside `<script>` tags, and that the regular `json` filter should be
 used everywhere else. Functionally this would be identical to using
 `json|safe`, but it is more semantically obvious what the author is trying
 to do, and it would be easier to document correct usage.

--
Ticket URL: <https://code.djangoproject.com/ticket/17419#comment:27>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/061.2c20812d848dcee5e4d55b1b446b97f2%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to