Mass Assignment - Security Part 10

NB: This is the tenth post in a series of posts on web application security.

“Mass assignment”? That’s a Rails thing!

GitHub was the recent, high-profile target of an “attack”—it wasn’t so much a vicious attack as a “hey you guys, this is serious” attack, really gray-hat at its darkest—that made use of a feature in Rails called Mass Assignment.

So why, in a series of posts ostensibly about Django, am I talking about a feature in Rails?

Because Mass Assignment, and the underlying vulnerability, boil down to a lack of whitelisting, and that’s something that any application, Rails, Django, or otherwise, can be susceptible to. You want to limit what your users can change to what they’re allowed to change.

Imagine a Django developer who did something along the lines of this:

Fortunately, that doesn’t quite work in the same way. Django has some built-in protections, but you can see there are obviously some risks involved, especially in the update example, since we’re operating on a QuerySet instead of an object itself, and have access to an update() method.

The correct way to do this sort of thing in Django is to use Forms. There are always going to be special cases and weird requirements, but forms are almost always the right thing to do. The trick is just to use them the right way. Compare these two examples:

This is a step in the right direction. This creates a form that does a bunch of basic validation on the types of data allowed. But really, it doesn’t do much beyond what the model validates itself. At the very least, you’ll want to do something like this:

This whitelists the fields the user can change. (There is also an excludes property that let’s you blacklist fields. Don’t use it, prefer fields.)

Now that you’ve got your ModelForm instance, you can add all sorts of custom validation logic. Django gives you “your age must be an integer.” You can insist “and you need to be at least 21.” Then when you when you call the built-in is_valid() method, the form will run all of this validation for you, and only let users change what you let them, to what you let them. A very, very simple example:

If there is anything you take from this post, let it be these 4 simple things:

  1. Django isn’t quite as susceptible to mass assignment as Rails, but that doesn’t mean you’re in the clear.
  2. Read the Django forms docs and use them.
  3. Use the fields property to whitelist what can be changed.
  4. Forms may seem like boilerplate but they’re a maintenance win in the long run. Especially when you start adding custom validation.

And just be careful anytime you let someone create or modify objects—this goes for anyone in any app in any framework. Use whitelists, not blacklists, and certainly not nothing, to decide who can change what.

Next up, we’ll get to some interesting issues from my own experiences here. This intermission won’t be quite as long, I promise.