SQL injection. If you use Django's object-relational mapper (ORM) layer, you are basically protected from SQL injection.
The only caveat is that you need to avoid manually forming SQL queries using string concatenation. For instance, do not use raw SQL queries (e.g., raw()
). Similarly, do not use the extra()
method/modifier to inject raw SQL. Do not execute custom SQL directly; if you bypass Django's ORM layer, you bypass its protections against SQL injection.
CSRF. Django's built-in CSRF protection is good. Make sure you enable it and use it everywhere. Django provides ways to disable it locally or globally; obviously, don't do that.
It is important that you make sure that GET requests do not have any side effects. For requests which can have a side-effect, make sure you use a POST request (and do not accept a GET request for those). This is standard web design, but some developers screw it up; Django's built-in CSRF prevention assumes you get this right.
There are some caveats if you have subdomains (e.g., your web app is hosted on www.example.com
and there is a subdomain alice.example.com
that hosts user-controlled content); the built-in CSRF protection might not be sufficient in that case. That's a tricky case for a number of reasons. Most web applications won't have to worry about these caveats.
XSS. If you use Django's template system and make sure that auto-escaping is enabled, you're 95% of the way there.
Django provides an auto-escaping mechanism for stopping XSS: it'll automatically escape data that's dynamically inserted into the template, if it hasn't already been escaping (see e.g., mark_safe
, safe
, etc.). This mechanism is good stuff, but it is not quite enough on its own. It takes you much of the way to stopping XSS, but you still have to be aware of some issues.
In particular, Django's auto-escaping will escape data sufficiently for most HTML contexts, but in some special cases you must manually do additional escaping:
Make sure you quote all attributes where dynamic data is inserted. Good: <img alt="{{foo}}" ...>
. Bad: <img alt={{foo}} ...>
. Django's auto-escaping isn't sufficient for unquoted attribute values.
For data inserted into CSS (style
tags and attributes) or Javascript (script
blocks, event handlers, and onclick
/etc. attributes), you must manually escape the data using escaping rules that are appropriate for CSS or Javascript.
For data inserted into an attribute where a URL is expected (e.g., a href
, img src
), you must manually validate the URL to make sure it is safe. You need to check the protocol against a whitelist of allowed protocols (e.g., http:
, https:
, mailto:
, ftp:
, etc. -- but definitely not javascript:
).
For data inserted inside a comment, you have to do something extra to escape -
s. Actually, better yet, just don't insert dynamic data inside a HTML comment; that's an obscure corner of HTML that's just asking for subtle problems. (Similarly, don't insert dynamic data into attributes where it is crazy for user input to appear (e.g., foo class=...
).)
You still must avoid client-side XSS (also known as DOM-based XSS) separately; Django doesn't help you with this. YOu could read Adam Barth's advice on avoiding XSS for some guidelines that will help you avoid client-side XSS.
If you use mark_safe
to manually tell that Django that some data has already been escaped and is safe, you'd better know what you're doing, and it really better be safe. It's easy to make mistakes here if you don't know what you are doing. For example, if you generate HTML programmatically, store it into the database, and later send it back to the client (unescaped, obviously), it's very easy to make mistakes; you're on your own, and the auto-escaping won't help you.
The reason why you have to do something extra for these cases is that Django's auto-escaping functionality uses an escaping function that is supposed to be one-size-fits-all, but in practice, one size doesn't always fit in every case; in some cases, you need to do something extra on your own.
For more information about what escaping rules to use in these special contexts, see the OWASP XSS cheatsheet.
Other stuff. There are some other things you didn't mention:
For more information. See Django's documentation on security, including the chapter on security in the Django book.