Django ¶
Since Django is such a large project we divide it into its components for security analysis.
The Django book has a great chapter on many of the security concerns in Django.
Components¶
Access Control/Authorization¶
See access control.
What mechanisms are there to restrict URL access?
- You can always return a custom response from a view function with an appropriate status code (e.g. 403 or 404).
- There are builtin decorators for view function to restrict access for a view/url to logged in users, users with a special permission, users who can login into the admin panel, superusers, users that pass a test (test means a function gets a user instance and must return True to grant access).
What permissions model is available?
- Permissions are implemented per database table. There are three default permissions per model: can_change, can_create, can_delete permissions.
- Django's has support for per-object (per-row) permissions but the default implementation doesn't use them - this means a third party module could hook into the system to provide logic for per-object permissions.
How hard is it to define who should be able to access what?
- It's very easy to define custom view decorators that can be reused for all views where you need to restrict access.
Authentication¶
See authentication.
What authentication methods are available?
- The users can authenticate with username/password credentials per default. Django provides the possibility to write your own authentication backend which allows authentication to every possible backend - like .htpasswd files from apache, a LDAP directory or the unix passwd/shadow files.
What hash method is used on user credentials? Is it salted?
- Yes the password credential is salted. The default algorithm is sha1. But its possible for an application developers to use md5 instead of sha1 - but I never saw any project that changed the default here. It's not obvious to change it.
Here is the default password hashing from version 1.2.1:
240 def set_password(self, raw_password):
241 import random
242 algo = 'sha1'
243 salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
244 hsh = get_hexdigest(algo, salt, raw_password)
245 self.password = '%s$%s$%s' % (algo, salt, hsh
16 def get_hexdigest(algorithm, salt, raw_password):
17 """
18 Returns a string of the hexdigest of the given plaintext password and salt
19 using the given algorithm ('md5', 'sha1' or 'crypt').
20 """
21 raw_password, salt = smart_str(raw_password), smart_str(salt)
22 if algorithm == 'crypt':
23 try:
24 import crypt
25 except ImportError:
26 raise ValueError('"crypt" password algorithm not supported in this environment')
27 return crypt.crypt(raw_password, salt)
28
29 if algorithm == 'md5':
30 return md5_constructor(salt + raw_password).hexdigest()
31 elif algorithm == 'sha1':
32 return sha_constructor(salt + raw_password).hexdigest()
33 raise ValueError("Got unknown password algorithm type in password.")
Note:
This scheme uses random.random(). The random module uses the deterministic Mersenne Twister algorithm to generate random numbers. This is fine for most purposes, but it is not suitable for cryptographic purposes. It is better to create a random.SystemRandom instance to get random data suitable for cryptography. A patch has been submitted to this ticket for this issue and for the length of the salt.
Note:
MD5 is unacceptable. SHA-1 is acceptable, but not ideal. At this point there are theoretical attacks that can find collisions in time that is "within the realm of computational possibility". It is recommended that one of the SHA-2 algorithms be used.
Note:
The most concerning thing in the above algorithm is that a salt of only 5 hexadecimal characters is used. This is just over a million possible salts (20 bits). We'd really like to see something closer to our recommendation of 64 bits. Django is tracking this issue here.
Could account creation overwrite an existing account?
- Not by default since usernames are unique and primary keys are sequential numbers from the Database. However a programmer can ofcourse always delete, replace and create new users in the code.
Could the change password or recover password functions be used maliciously?
Configuration¶
See configuration.
Are the framework dependencies up to date? Are there security vulnerabilities in the versions used by the framework?
- There are no hard dependencies for django. However django ships with some utility modules that where merged in from other sources so it might be worth to check if these sources in django are up to date.
Are there default account passwords?
- No.
If there an exception handler, will it run in production? Are there notes and reminders to turn it off in configuration?
- There is an exception handler with a debug page showing your the traceback, content of local variables in the stack traces etc. This is turned off by a setting called DEBUG which should be obvious to users that this settings needs to be set to False in production.
Cross-Site Request Forgery (CSRF)¶
Are there GET requests that change state? This helps enable CSRF.
- Not in the applications that are shipped with django in the contrib directory. An application developer can of course make mistakes and use GET for changing state. But I think this is made clear in the related parts of the documentation.
Django CSRF weakness in versions prior to 1.2.5 or 1.1.4
The CSRF protection in older versions of Django relied upon a token that is added as a hidden field for POST requests. For AJAX requests a token is not added. Instead the header X-Requested-With is added and the django framework validates that the header is present. This approach works since cross origin requests are unable to influence the headers sent by a user.
However, recently discovered vulnerabilities in third party plugins have allowed attacks that can forge headers and render this approach null. The risk is that CSRF attacks would still be possible against AJAX calls for targeted victims that are running the vulnerable plugins.
Starting with version 1.1.4 and 1.2.5 Django uses CSRF tokens for form POSTS and AJAX requests.
Django 1.2.5 release notes - http://docs.djangoproject.com/en/1.2/releases/1.2.5/
Cross-Site Scripting (XSS)¶
See cross-site scripting.
What specific escaping does the template engine do to prevent XSS? What does the developer need to do to use it? What is automatic and what is manual?
- The template engine escapes all variables used in the template by default. If you can trust the content of a variable you can mark it as safe manually - either in the template directly or in python code before you add the variable to the template's context.
Are there vulnerabilities in the template engine?
Well.... yes.
Two templates tags of django are unsafe to use: both firstof and cycle do not auto-escape.
- Bug and Bug2
- Ticket to document that these tags dont auto-escape
- django documentation on firstoff & cycle
unsafe example:
#unsafe! this is not auto-escaped
{% firstof var1 var2 var3 %}
safe example:
{% filter force_escape %}
{% firstof var1 var2 var3 "fallback value" %}
{% endfilter %}
Cryptography¶
See cryptography.
We'd like to see frameworks provide the structure to do solid encryption in applications even if the framework itself doesn't need it. A configuration toggle to encrypt stored user data (if such a thing is handled by the framework) would be wonderful as well.
Does the framework provide a system for applications to encrypt data?
- Not by default yet. There is (was?) an attempt by a core developer (Simon Willison) to provide support for encrypted and signed data and signed ookies.
What algorithm is it using?
Where is the key stored?
- There is a setting called SECRET_KEY in the settings (python) file. This can be used for cryptographic use (and is used by the implementation mentioned above).
Who can access the encrypted data?
Can developers easily encrypt user data?
Injection¶
See injection.
What interpreters is the framework capable of using?
- Django runs usually on C implementation of Python (CPython) but also supports other interpreters like Jython and PyPy.
How does it access a database? What about LDAP queries or OS commands?
- Django is using its own ORM abstraction layer to hide database access. The normal API makes SQL injections very difficult - you have to drop down to 'raw' methods to supply your own SQL.
- LDAP queries are not supported by django - maybe be by thirdparty modules.
- OS commands are not triggered in django core. However they are ofcourse possible by using python's os module - but thats in the responsibility of the app developers.
What escaping is done on data headed to an interpreter?
- Django doesn't use eval or exec statements. No input made from the browser is evaluated in the context of an interpreter.
- Django's session backend uses python's pickle module to serialize data from the session. The data is signed using HMAC and the signature checked before unpickling, and therefore cannot be tampered with by the client. The database session backend, which is the default, provides additional protection since a client cannot tamper with the stored session data at all since it is stored on the server.
Can a developer write raw SQL?
- A developer can of course write raw SQL by using the standard database connection, or by using the 'raw' method available on ORM objects. The API provides a safe way to pass the parameters needed for the query, and the documentation makes it very clear that string interpolation of parameters must not be done. https://docs.djangoproject.com/en/dev/topics/db/sql/
- There is still another method to write raw SQL while using the ORM. It provides hooks to inject your own WHERE clauses or SELECT fields. Django has support for escaping parameters you pass into this extra SQL - but you are not forced to escape them. It's pointed out in the documenation that you should escape them but only for compatibility with different DB backends not as protection against SQL Injection. See https://docs.djangoproject.com/en/dev/ref/models/querysets/#extra
Object Reference¶
See object reference.
Is there a mechanism to proxy direct object references through an indirect reference?
Redirects and Forwards¶
If there is skeleton code in the framework, can a URL be crafted to redirect to an arbitrary site?
In the framework documentation for doing redirects and forwards, is there a warning about using user-supplied data in the redirect location?
Session Management¶
See session management.
How are session IDs generated?
Are session IDs ever exposed in the URL? Leaking session IDs through logs, referrer headers, etc can lead to compromised accounts.
- No. Session IDs are only stored in a cookie and in the database.
Are session IDs accepted through GET or POST data? This would aid in session fixation attacks.
- No. Session IDs are only accepted as cookie.
Do session IDs timeout?
- This is configurable by a setting. The default is that the session times out after 2 weeks.
- Another setting allows the session cookie to time out when the browser is closed. This is turned off by default.
Can users log out?
- Yes.
When a user logs out, is the session invalidated?
Yes. See line #89 of version 1.2.1's contrib.auth.
Are session IDs changed after login?
- Yes.
Transport Layer Security¶
Can the framework be used with SSL? What is required and how difficult is it to set up?
- It is possible. (Have no information about setting it up.)
When used in SSL mode, do session cookies have the 'secure' flag set?
- Yes if you set a specific setting. (See http://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure)
Validation¶
See validation.
Is there a mechanism available that allows developers validate input data against a schema? Regex validation as well as custom attributes like numeric comparison. Having developers directly use the re module does not count.
- Django provides a very mature and flexible forms framework which can be used to validate any input data. This is used by nearly all developers to validate input.
Validating Images¶
Django has a validator, the ImageField.is_valid() method. However, this validator does not check the file extension. It has been demonstrated that it is possible to use EXIF comments to pass the validator but make files that will be parsed by other interpreters such as PHP.
Be aware that apps like Django_avatar and Pinax use the ImageField validator.
-
Wiki content is available under a Creative Commons 3.0 License.
