John Hess
At the end of this talk, you should be able to:
We're not just talking either. Get a laptop out (phones will work, too)
a.k.a. Someone else is running JavaScript on your pages.
What kind of havoc could a baddie wreak if they got to insert any JS they wanted into pages visited by your users?
What kind of havoc could a baddie wreak if they got to insert any JS they wanted into pages visited by your users?
Visited by your admins?
...nobody ever makes a quick and dirty admin page that goes largely unscrutinized...
...but this is a Django meetup. I don't wanna hear about front-end nonsense!
Too danged bad. Turns out this is your problem. Not because you can't trust your front end engineers, but because you might perpetrate this yourself -- even if your site has no JS at all!
def greeter(request):
who = request.GET.get('name', 'friend')
return HttpResponse("Hello, {}".format(who))
http://vcwa.veryveryvulnerable.com
[disclaimer: VCWA is a flask app running similar code.]
No TLS! The horror! Turns out the game is already over :-P
def greeter(request):
who = request.GET.get('name', 'friend')
return HttpResponse("Hello, {}".format(who))
http://vcwa.veryveryvulnerable.com
def greeter(request):
who = request.GET.get('name', 'friend')
return HttpResponse("Hello, {}".format(who))
http://vcwa.veryveryvulnerable.com
<script>alert('im in ur pages, runnin my own js')</script>
# Flask code, the horror! This is ~a django view.
@app.route('/stored', methods=['GET', 'POST'])
def stored_xss():
# store the new message, if there is one
if request.method == 'POST':
store_message(request.form.get('message'))
# either way, give back the list of messages
return render_template(
'guestbook.html',
messages=get_messages()
)
http://vcwa.veryveryvulnerable.com (visit the guestbook)
Audience participation: someone please exploit this :-)
No TLS! The horror! Turns out the game is already over _-P
{% for message in messages %}
<!-- tell our templating engine not to escape
any HTML. This way, our users can post
stylish stuff -->
{% autoescape off %}
<li>{{ message }}</li>
{% endautoescape %}
{% endfor %}
http://vcwa.veryveryvulnerable.com (visit the guestbook)
Audience participation: someone please exploit this :-)
No TLS! The horror! Turns out the game is already over _-P
Again, we stress that [auto-escaping] is on by default. If you’re using Django’s template system, you’re protected.
--Django docs
Safe:
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
Unsafe:
{% for message in messages %}
<li>{{ message | safe }}</li>
{% endfor %}
{% autoescape off %}
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
{% endautoescape %}
Unsafe:
// Just a sampling... there are many more of each
$("<div>" + someText + "</div>"); // jquery
$("#some-id").html(someText); // jquery
element.innerHTML = someText; // plain ol' js
The key to understanding CSRF attacks is to recognize that websites typically don't verify that a request came from an authorized user. Instead they verify only that the request came from the browser of an authorized user.
-- Bill Zeller
Again, attackers are using the trust you have in a user to do anything a user can do. Exfiltrate data, change passwords, etc.
# Flask code, the horror! This is ~a django view.
@app.route('/stored', methods=['GET', 'POST'])
def stored_xss():
# store the new message, if there is one
if request.method == 'POST':
store_message(request.form.get('message'))
# either way, give back the list of messages
return render_template(
'guestbook.html',
messages=get_messages()
)
// embedded on any old page you can get the victim
// to visit
console.log('muahaha...');
$.post(
'http://vcwa.veryveryvulnerable.com/stored',
{
message: 'POST with any payload the attacker
wants... on behalf of a real user...'
}
);
Django actually protects you a bit.
# in the default settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
So, as long as your server requires proof that whatever's making the request can read from a protected resource, you're in pretty good shape.
That's what the CsrfViewMiddleware
does.
It puts a random CSRF token in a cookie (and sometimes, a form). You can only read that token if you are requesting from the same domain.
The new and improved guestbook 2.0 (http://ssda.veryveryvulnerable.com)
The same old attack doesn't work: http://jthess.com/vvv/csrf2.html
CSRF Middleware Failure Modes
CSRF Middleware Failure Modes
CSRF Middleware Failure Modes
The victim:
# django view
def nuke(request):
"""Clears the guestbook message board."""
# ...check that user is admin/superuser...
get_db().flushall()
return HttpResponse('database nuked')
The attack:
<!-- Anywhere I can get an admin to visit... maybe a
support forum? -->
<img src="http://ssda.veryveryvulnerable.com/nuke/"/>
CSRF Middleware Failure Modes
ALLOWED_HOSTS
to *
... right?What to do if you're in that situation or might be:
Thanks to Morgan Edwards and Ryan Chan
everything's at http://veryveryvulnerable.com (and either or )
The public suffix list is why you can't do this on heroku, appengine, and the like.
SQL injection isn't XSS. But they're cousins of sorts. If you understand SQL injection, you start to get a sense of what happens when user input becomes code.
instead of being SQL, it's JS. instead of being executed on the DB server, it's being executed in users' browsers.
Table of contents | t |
---|---|
Exposé | ESC |
Autoscale | e |
Full screen slides | f |
Presenter view | p |
Source files | s |
Slide numbers | n |
Blank screen | b |
Notes | 2 |
Help | h |