Stephen from Mezzanine here - thanks for the thorough response Russ.

The cleansing process we go through is very rigorous - we're leaning
on the shoulders of tools that have solved this problem (in our case
the bleach library). It uses a white-list of tags and attributes, so
all those tricky edge cases around event handlers as attributes are
solved with a well-documented white-list based on known XSS vectors.

The reality of it is though, is that you're going to have projects
where the people paying for it want to be able to add their own HTML
content, scripts included. In this case I feel the correct approach is
to give them this option, and educate them on the consequences, and
subsequent level of trust required for anyone they give access to.

So we've ended up defining a setting with various cleansing levels -
the default is high, which will strip out any possible vector via tag/
attribute. The next is medium, which will allow things such as tags
required for embedding video. We had multiple reports within days of
adding the cleansing process: "help! I've updated to the latest
version and I can't add videos anymore". With this level we still
remove scripts and known event handling tag attributes. There's
probably an exploitable vector even with the tags and attributes we
allow simply for embedding videos. Then the final level disables
cleansing entirely - you can turn it off, please know what you're
doing (with very loud warnings shown around this).

I fully appreciate the technical approach of never trusting user
content, and if I had it my way, this is the path we would take. But
in reality it just doesn't cut it. Users need these features, and
every scenario is going to be different - different content
requirements, different user structures. I feel like the approach
we've taken is the closest we can get to balancing security and
usability.


On May 12, 12:13 pm, Russell Keith-Magee <russ...@keith-magee.com>
wrote:
> On Sat, May 12, 2012 at 5:11 AM, Josh Cartmell <joshcar...@gmail.com> wrote:
> > I work a lot with Mezzanine which is a CMS that uses Django.  A
> > security issue was recently revealed where an admin user, lets call
> > him A, (they can post rich content) could put a cleverly constructed
> > javascript on a page such that if a superuser, let's call her B, then
> > visited the page it would elevate A to superuser status (a more
> > thorough explanation is here:
> >http://groups.google.com/group/mezzanine-users/browse_thread/thread/1...).
> > Apparently any django app which allowed admin users to post arbitrary
> > html would be vulnerable.
>
> > My first thought was that csrf protection should prevent this but alas
> > that is not the case.  The only real solution found is to restrict
> > admin users from posting any javascript in their content, unless you
> > completely trust the admin users.
>
> This isn't a CSRF issue. CSRF stands for Cross Site Request Forgery. A
> CSRF attack is characterised by:
>
>  * A user U on site S, who has credentials for the site S, and is logged in.
>
>  * An attacking site X that is visited by U.
>
>  * Site X submits a form (by POST or GET) directly to site S; because
> U is logged in on S, the post is accepted as if it came from U
> directly.
>
> CSRF protection ensures that site X can't submit the form on the
> behalf of U - the CSRF token isn't visible to the attacker site, so
> they can't provide a token that will allow their submission to be
> accepted.
>
> What you're referring to is an injection attack. An injection attack
> occurs whenever user content is accepted and trusted on face value;
> the attack occurs when that content is then rendered.
>
> The canonical example of an injection is "little johnny 
> tables":http://xkcd.com/327/
>
> However, the injected content isn't just SQL; all sorts of content can
> be injected for an attack. In this case, you're talking about B
> injecting javascript onto a page viewed by A; when A views the page,
> the javascript will be executed with A's permissions, allowing B to
> modify the site as if they A.
>
> Django already has many forms of protection against injection attacks.
> In this case, the protection comes by way of Django's default template
> rendering using escaped mode. If you have a template:
>
> {{ content }}
>
> and context (possibly extracted from the database):
>
> <script>alert('hello')</script>
>
> Django will render this as:
>
> &lt;script&gt;alert('hello')&lt;script&gt;
>
> which will be interpreted as text, not as a script tag injected into your 
> page.
>
> That said, the protection can be turned off. If you modify the template to 
> read:
>
> {{ content|safe }}
>
> or
>
> {% autoescape off %}
> {{ content }}
> {% endautoescape %}
>
> or you mark the incoming string as "mark_safe" in the template
> context, then the content will be rendered verbatim -- which means
> that the Javascript will be executed.
>
> I'm not intimately familiar with Mezzanine or DjangoCMS, but based on
> the nature of those tools (i.e., tools for building end-visible
> content), I'm guessing they've marked content as safe specifically so
> that end users can easily configure their CMS sites by putting HTML
> into a field somewhere on the site. The side effect is that they're
> implicitly saying that *all* user-supplied content is safe, which
> provides the channel by which an attacker can do his/her thing.
>
> The lesson from this? Even when you think you can trust a user's
> content, you can't trust a user's content :-)
>
> > My question is are there any other solutions to these sorts of
> > problems?  It seems like allowing an admin user to post javascript is
> > reasonable, what is unreasonable is for that javascript to be able to
> > elevate a user's privilege.  Could improvements be made to the csrf
> > mechanism to prevent this sort of user privilege elevation?
>
> As I've indicated, there is a solution, and Django already implements
> it. It involves escaping content, and has nothing to do with CSRF.
>
> In the case of Mezzanine, they've fixed the problem by implementing a
> 'cleansing' process - i.e., still accepting the content as 'safe', but
> post-processing it to make sure that it really *is* safe, by stripping
> out <script> tags or anything else that might provide an injection
> channel.
>
> While I can fully understand why Stephen has taken this approach for
> Mezzanine, I'm not convinced it's a good answer in the general case.
> CMS solutions are an edge case -- they actually *want* to accept HTML
> content from the end user, so that it can be rendered.
>
> The problem with cleansing is that it doesn't fix the problem -- it
> just narrows the attack window. Ok, so lets say your cleanser removes
> <script> tags; that's fixed one obvious way to inject. But what about
> <a href="…" onclick="alert('hello')"> That's still javascript content
> that could be used for an attack; your attacker just needs to socially
> engineer the user to click on the link. So, you update your cleaners
> to strip onclick attributes -- at which point, the attacker finds a
> new way to inject, or they find a bug in your cleansing library, or
> they find the one input field on your site that you accidentally
> forgot to cleanse…  you're now engaged in an arms race with your
> attackers.
>
> The default Django position of "don't *ever* trust user content" is
> ultimately the safest approach, which is why Django implements it.
> Django does provide a way to disable that protection, but it really
> should be done as a last resort.
>
> That said -- we're always open to suggestions. If anyone has any good
> ideas for preventing injection attacks (or any other type of attack,
> for that matter), let us know. You can't have enough out-of-the-box
> security.
>
> Yours,
> Russ Magee %-)

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.

Reply via email to