00:00:00

Live-Fire Exercises: Security in Django

John Hess

Notes

Objective

At the end of this talk, you should be able to:

  • Perpetrate two common types of attacks
  • Use Django's built-in protections
  • Identify when those protections aren't good enough
  • Do something better when they aren't good enough

Notes

Attacks

  • XSS (Cross Site Scripting)
    • Simpler to understand/perpetrate
    • Giant catastophe if it happens
  • CSRF (Cross Site Request Forgery)
    • A bit more subtle
    • Slightly less giant catastophe if it happens
  • Both take advantage of the trust you have in your users and your users in you
    • Don't trust people.

Notes

What aren't we talking about today?

  • Nation states & zero days
  • Countless other footguns that surround you
    • package management
    • JS libraries you're loading from a CDN
    • ...
  • Why CSRF and XSS start with the same word, but are abbreviated differently

Notes

Get out a laptop

We're not just talking either. Get a laptop out (phones will work, too)

Notes

[a moment of prayer]

Notes

XSS

Notes

XSS: Cross-Site Scripting

a.k.a. Someone else is running JavaScript on your pages.

Notes

XSS: Cross-Site Scripting

Pop Quiz:

What kind of havoc could a baddie wreak if they got to insert any JS they wanted into pages visited by your users?

Notes

XSS: Cross-Site Scripting

Pop Quiz:

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...

Notes

XSS: Cross-Site Scripting




...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!

Notes

Notes

Notes

Live Fire: Reflected XSS

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

Notes

Live Fire: Reflected XSS

def greeter(request):
    who = request.GET.get('name', 'friend')
    return HttpResponse("Hello, {}".format(who))

http://vcwa.veryveryvulnerable.com

  • "John Baldwin" (our gracious organizer)

Notes

Live Fire: Reflected XSS

def greeter(request):
    who = request.GET.get('name', 'friend')
    return HttpResponse("Hello, {}".format(who))

http://vcwa.veryveryvulnerable.com

  • "John Baldwin" (our gracious organizer)
  • <script>alert('im in ur pages, runnin my own js')</script>

Notes

Notes

Notes

Notes

Notes

Notes

Live Fire: Stored XSS

# 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

Notes

Live Fire: Stored XSS

{% 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

Notes

XSS: Django is your friend

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 %}

Notes

XSS: Django is your friend

Unsafe:

{% for message in messages %}
    <li>{{ message | safe }}</li> 
{% endfor %}

{% autoescape off %}
{% for message in messages %}
    <li>{{ message }}</li>
{% endfor %}
{% endautoescape %}

Notes

XSS: Frontend DOM Manipulation Requires the Same Love & Care

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

Notes

XSS: Summary

  • Do. Not. Trust. User. Input.
  • Escape user input when you render it. Every time.
  • Just because it came from your server as JSON doesn't mean it's not user input.
  • Templating is your friend.
    • Django templating server-side
    • Safe methods or client-side templating

Notes

CSRF

Notes

Notes

Notes

CSRF: Cross Site Request Forgery

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.

Notes

Notes

Live Fire: CSRF (back to the guestbook)

# 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()
    )

Notes

Live Fire: CSRF

// 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...'
    }
);

http://jthess.com/vvv/csrf.html

Notes

Notes

Notes

CSRF: Cross Site Request Forgery

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',
]

Notes

Notes

CSRF: Cross Site Request Forgery

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.

Notes

CSRF: Cross Site Request Forgery

The new and improved guestbook 2.0 (http://ssda.veryveryvulnerable.com)

The same old attack doesn't work: http://jthess.com/vvv/csrf2.html

Notes

CSRF: Cross Site Request Forgery

CSRF Middleware Failure Modes

Notes

CSRF: Cross Site Request Forgery

CSRF Middleware Failure Modes

  • Make sure it's turned on.
    • Django Rest Framework doesn't check your CSRF token unless you're using SessionAuthentication :-|

Notes

CSRF: Cross Site Request Forgery

CSRF Middleware Failure Modes

  • Make sure it's turned on.
    • Django Rest Framework doesn't check your CSRF token unless you're using SessionAuthentication :-|
  • Misusing "safe" HTTP Methods

Notes

CSRF: Cross Site Request Forgery

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/"/>

Notes

Live Fire: Abusing Guestbook 2.0

jthess.com/vvv/csrf3.html

Notes

CSRF: Cross Site Request Forgery

CSRF Middleware Failure Modes

  • Make sure it's turned on.
    • Django Rest Framework doesn't check your CSRF token unless you're using SessionAuthentication :-|
  • Misusing "safe" HTTP Methods
  • Trusting cookies you don't control

Notes

Notes

Live Fire: (Still) Abusing Guestbook

evil.veryveryvulnerable.com

Notes

CSRF: Cross Site Request Forgery

  • What else is running on this domain?
    • Could it be evil?
    • Could it be vulnerable?
      • Old marketing sites
      • Wordpress blog
  • Could your site be running on another domain?
    • You would never set ALLOWED_HOSTS to * ... right?

Notes

CSRF: Cross Site Request Forgery

What to do if you're in that situation or might be:

  • Store the CSRF token connected to the user's session on the server-side and check against that tamper-proof value
  • Cryptographically sign a user/session-associated token with a private key, and, upon the token's return, use that to check that you are the one who generated the value

Notes

Questions?

Notes











Thanks to Morgan Edwards and Ryan Chan

everything's at http://veryveryvulnerable.com (and either or )

Notes

One Last Fun Fact

The public suffix list is why you can't do this on heroku, appengine, and the like.

Notes

Backup Slides

Notes

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.

Notes

Notes

Other things to talk about

  • The "public suffix list"

Notes