Mass Assignment Vulnerability in Django

I was reviewing the Django 1.6 Alpha Release Notes and came across a mention in the deprecated features section on a possible mass assignment vulnerability.

Previously, if you wanted a ModelForm to use all fields on the model, you could simply omit the Meta.fields attribute, and all fields would be used.

This can lead to security problems where fields are added to the model and, unintentionally, automatically become editable by end users. In some cases, particular with boolean fields, it is possible for this problem to be completely invisible. This is a form of Mass assignment vulnerability.

For this reason, this behaviour is deprecated, and using the Meta.exclude option is strongly discouraged. Instead, all fields that are intended for inclusion in the form should be listed explicitly in the fields attribute.

I hadn’t paid attention to the mass assignment kerfuffle in the Rails community last year, so I wasn’t familiar with this issue. A little reading convinced me of the problem and how I can protect myself.

Basically, this vulnerability arises when database fields that are intended to be updated only through special processes are open to general update processes. An example would be an expedite_order field on an Order model. We don’t want our customers checking this box on every order, or the shipping department would be overwhelmed with rush work.

There are several ways in Django to avoid this problem. Let’s discuss them:

  1. Don’t include this special field in the template – I think we all know that it would be pretty easy for a template designer to use the as_p() method of the form to generate a quick template without knowing about the desired limited access for this field. It happens. One also has to worry about the malicious user that crafts a POST response that includes the field in question.

  2. Use the exclude attribute in the Meta class of the form – OK, this is better – our field is successfully hidden away. However, the vulnerability can return when the underlying model is updated to include a new special field. Unless the developer also updates the exclude lists for all forms related to this model, the new field is now accessible.

  3. Use the fields attribute in the Meta class of the form – Now we’re getting somewhere! Only those field explicitly listed for inclusion in the form will be accessible. When a new field is added, it will not be available to the form unless the developer updates the list.

The Django team recommends that option 3 be the solution of choice, but will accept option 2 as well. Future versions of Django will require either the fields or exclude attribute in ModelForms.

The same vulnerability exists in class-based views that use the ModelFormMixin, including CreateView and UpdateView. When these classes are used, the developer must include either a fields or exclude attribute when declaring a model, or use a ModelForm that does.

Thanks to the Django developers for continuing to make Django one of the safest frameworks around!