Building secure Django websites - Erik Romijn

Tags: django, pun

(Talk at the 2012-01-18 Dutch Django meeting)

Security is about three things:

  • Integrity of your data. I should not be able to change it.

  • Confidentiality: can I access what I should not be able to access?

  • Availability: can I break your website?

A well-known bad case is DigiNotar. Malware on most of their computers, including the most secure once. Their digital certificates were compromised and the company went bankrupt in three weeks time.

One thing that we have to take care of regarding Django: cookies. Cookies are stored in your browser and are linked to a website. All requests to that website get the cookies in every request. In Django, your session ID is stored in your cookie. If you get hold of the session ID, you get hold of the user’s session. Django stores the actual session’s data in the database, so the actual data is safe.

In Django 1.4 there are cookie-based sessions, but they’re a bad idea: never use them. He doesn’t know why they even build it in. UPDATE: they’re not that bad, see the comments.

CSRF: cross site request forging. Tricking the browser into posting requests to other websites. http://your-router/set_password?pwd=hamster . Django protects you by adding a secred csrf_token to every POST form and checking for it. So you’re safe.

XSS injection. Cross site scripting. Happens when you don’t filter/escape user submitted content. So you could send a <script> tag with a script that for instance steals every cookie on your server for stealing session IDs. Common is tricking a single user into running the script. A more powerful, but rarer, way is to get a script on some website and trick everyone visiting the site…

Django is mostly safe as it escapes template tag output by default. There is a rare vulnerability if you don’t put double quotes around html attributes. Everything after a space in var in <p class={{ var }}> ends up as a separate attibute…

Cookie security is pretty good in Django. 1.4 sets SESSION_COOKIE_HTTPONLY to true. You ought to do this always. And if you always use https, set CSRF_COOKIE_SECURE and SESSION_COOKIE_SECURE to true.

And… don’t trust in a regex that filters dirty tags out of your user-submitted code. There are SO many tricks and ways to circumvent any regex. Use markdown or something like that.

SQL injection is common, but Django has no know exploits. It is rock solid. But there are other ways. For instance LDAP injection. Don’t put an unfiltered username into your LDAP query as you can hack the query. Or direct user input into filenames. And what about shell injection? some_filename.txt;rm -rf… Shell calls are always very risky, but Python’s subprocess() helps a lot.

There’s a difference between validation and escaping. Validation is done as early as possible. Escaping is done as late as possible. Both serve a different purpose. As always: whitelisting is better than blacklisting.

A new option is clickjacking that overlays a transparent website over another one, stealing your form fields and clicks. It works with iframes. Django 1.4 has a new X_FRAME_OPTIONS='DENY' that works with newer browsers.

And… Don’t trust browsers. If you disable something in javascript, a user can work around it, for instance. The speaker could always book rooms in school before anyone else could as the only-two-weeks-in-advance limitation was implemented in javascript, not in the backend…

Regarding tests: check login-protected pages as anonymous first. Check actions for the admin user first as anonymous, then as a regular user. Check it.

And if you run some test site that’s (temporarily) on an external IP address, you can be sure someone finds it. So watch out.

If you run backups, do you also run restores? Do you keep backups with a different provider? Once a provider was hacked and its servers were wiped. Including their backups. All customer data lost.

Django: don’t ever run it in DEBUG mode in production. It gives away way too much information. Another tip: name the admin url something different than /admin. it is a dead give-away that it is a Django website.

Watch out for other apps on the same server. Your Django server can be compromised by running some faulty-configured PHP code on the same server.

Always restrict database access per app.

Summary. Django helps out with CSRF, SQL injection and XSS; you need to take care of all other injection attacs (LDAP, filesystem, etc). Use the build-in Django security features. And never trust what the browser sends. And secure the web server itself as well. Make backups. logo

About me

My name is Reinout van Rees and I work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):