A "lazy" way to allow filtering on options is to pass request.GET
, request.POST
, request.query_params
as named parameters to the filter clause. For example with:
filter(**request.GET) MyModel.objects.
If the URL is for example /my-path?author_id=42
, then it will call MyModel.objects.filter(author_id=42)
, and thus it can filter on arbitrary data. As long as the data passed follows Django's ORM conventions, it can filter on all sorts of related models, and it is thus easily expandable.
Why is it a problem?
Exactly that, it is very flexible. This means that someone with some knowledge of Django can abuse that system to obtain sensitive data. Indeed, you could for example filter on: /my-path?author__secret__lte=M
, this can then be used to use binary search for example to "guess" the secret stored in a field named secret
that the Author
model might contain. The "hacker" can make a lot of requests and each time validate whether a certain object still appears in the result. If the secret is an alphanumerical string of hundred characters, it takes at most log2(63100)≈598 guesses to guess the secret. While this may look as a lot, this can easily be automated to guess secrets. A Slack API token is shorter and has a smaller alphabet, so that can be guessed in at most 183 requests.
Guessing a UUID, Slack API token, etc. this might be easier than expected. If the hacker makes some requests, one can easily find out, by looking when it returns a 404/500 error page how the modeling looks like, and eventually make a limited number of guesses to expose such secrets. It thus makes the system vulnerable to determine data you want to hide from an unauthenticated or different user.
What can be done to resolve the problem?
You can first preprocess the data container and look if request.GET
for example only contains keys that you consider valid. But that actually already is done: a package like django-filter
[GitHub] is specifically designed for that: it will only filter on the fields you define in the filter, and thus ignore the ones the hacker is trying to guess without permissions. The Django REST framework already works with django-filter
.
This package does not only allow to only filter with fields you think are safe, it can also clean input, such that true
indeed maps to True
and thus makes filtering more sensical, and can also define custom filtering options that perform more advanced filtering tasks. The most important part however is that it can hide the modeling from the user as well as prevent the user from guessing sensitive data.