Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to validate GET parameters in django

Tags:

python

django

I'm building a social website that uses django templates/dynamic pages (no SPA technology in place). I have some ajax calls that check the users news feed or new messages. Example GET web request of those looks as follows:

GET /feeds/check/?last_feed=3&feed_source=all&_=1500749662203 HTTP/1.1

This is how I receive it in the view:

@login_required
@ajax_required
def check(request):
    last_feed  = request.GET.get('last_feed')
    feeds = Feed.get_feeds_after(last_feed)

It all works, but I want to protect it so the function get_feeds_after does not crash when a malicious user sets the GET parameter to last_feed="123malicious4556". Currently it crashes because in the Feed model the function does this:

@staticmethod
def get_feeds_after(feed):
    feeds = Feed.objects.filter(parent=None, id__gt=float(feed))
    return feeds

and crashes with the error:

ValueError at /feeds/check/
invalid literal for float(): 2fff2

I currently solve this by directly performing checks on the GET variable and handling exception on int() casting:

def check(request):
    last_feed  = request.GET.get('last_feed')
    try:
        feed_source = int(request.GET.get('last_feed'))
    except ValueError:
        return HttpResponse(0)

My question is what is the best django-recommended way to address this? I know django has special support forms validation. But this does not seem quite right here, as the GET calls are more of an api rather than forms so it seems like a bad idea to define forms for those GET parameters.

Thanks

like image 743
Loop Back Avatar asked Jul 22 '17 19:07

Loop Back


People also ask

How do I validate fields in Django?

Django provides built-in methods to validate form data automatically. Django forms submit only if it contains CSRF tokens. It uses uses a clean and easy approach to validate data. The is_valid() method is used to perform validation for each field of the form, it is defined in Django Form class.

How do I get all query parameters in Django?

We can access the query params from the request in Django from the GET attribute of the request. To get the first or only value in a parameter simply use the get() method. To get the list of all values in a parameter use getlist() method.

What can be used to validate all model fields if any field is to be exempted from validation provide it in the exclude parameter?

clean_fields() method documentation: This method will validate all fields on your model. The optional exclude argument lets you provide a list of field names to exclude from validation. It will raise a ValidationError if any fields fail validation.


1 Answers

All you actually need are the form fields which do all basic validation for you. If you need custom validation you can write your own validator or even better your own custom form field.

To use the field alone without the form you can do like that for example:

evalType = forms.CharField().clean(request.GET.get('eval-type'))

Because calling this way is not very human friendly I prefer to write a function to deal with it:

def cleanParam(params, paramName, FieldType, *args, **kwargs):
    field = FieldType(*args, **kwargs)
    cleaned = field.clean(params.get(paramName))
    return cleaned

Which we use this way:

evalType = cleanParam(request.GET, 'eval-type', forms.CharField)

This will save you a form class. But I don't think it's very ugly to create a django form for that. A bit too much for the problem but no great concern IMHO.

The advantage of having a form is that you declare the fields you expect in your api call and can check all at once then see the result of is_valid().

I hope this helps.

like image 132
Eric Chiesse Avatar answered Sep 28 '22 01:09

Eric Chiesse